【Qt筆記】視圖選擇

選擇是視圖中經常使用的一個操做。在列表、樹或者表格中,經過鼠標點擊能夠選中某一項,被選中項會變成高亮或者反色。在 Qt 中,選擇也是使用了一種模型。在 model/view 架構中,這種選擇模型提供了一種更通用的對選擇操做的描述。對於通常應用而言,Qt 內置的選擇模型已經足夠,可是,Qt 仍是容許你建立本身的選擇模型,來實現一些特殊的操做。架構

 

Qt 使用QItemSelectionModel類獲取視圖中項目的選擇狀況。這個模型保持有項目的索引,而且獨立於任何視圖。這意味着,咱們可讓不一樣的視圖共享同一個選擇模型,歷來達到一種同步操做的目的。選擇由選擇區域組成。模型只將選區的開始和結束的索引位置記錄下來,以保證對於很大的選區也有很好的性能。非連續選區則由多個連續選擇組成。函數

選擇會直接應用於選擇模型所維護的那些被選中的索引上面。最新的選擇就是當前選擇。這意味着,即使界面上沒有顯示有任何項目被選擇,若是經過某些命令對選區進行操做,一樣會有做用。性能

在視圖中,始終存在一個當前項和被選擇項(即使從界面上看不到有任何選擇)。與一般所想的不一樣,當前項和選擇項是相互獨立的兩個狀態。一個項目能夠便是當前項又是選擇項。下表是當前項和選擇項的區別:code

當前項 選擇項
只能有一個當前項。 能夠有多個選擇項。
使用鍵盤或者鼠標點擊能夠改變當前項。 選擇項使用兩種狀態:選擇和未選擇,這取決於項目以前的狀態和其它一些設置,例如,單選或多選。只有在用戶進行交互的時候,這種狀態纔會發生改變。
當前項可使用 F2 或者鼠標雙擊進行編輯(前提是程序容許)。 當前項能夠結合另一個錨點指定被選擇或者去除選擇的一塊選區(或兩者的結合)。
當前項一般會有一個焦點框進行標識。 選擇項使用選區顏色進行標識。

在處理選擇的時候,咱們能夠將QItemSelectionModel當成數據模型中全部數據項的選擇狀態的一個記錄。一旦選擇模型建立好,這些數據項就能夠在不知道哪些項被選擇的狀況下進行選擇、取消選擇或者改變選擇狀態的操做。全部被選擇項的索引都在可隨時更改,其它組件也能夠經過信號槽機制修改這些選擇的信息。對象

標準視圖類(QListViewQTreeView以及QTableView)已經提供了默認的選擇模型,足以知足大多數應用程序的需求。某一個視圖的選擇模型能夠經過selectionModel()函數獲取,而後使用setSelectionModel()提供給其它視圖共享,所以,通常沒有必要新建選擇模型。索引

若是須要建立一個選區,咱們須要指定一個模型以及一對索引,使用這些數據建立一個QItemSelection對象。這兩個索引應該指向給定的模型中的數據,而且做爲一個塊狀選區的左上角和右下角的索引。爲了將選區應用到模型上,須要將選區提交到選擇模型。這種操做有多種實現,對於現有選擇模型有着不一樣的影響。文檔

下面咱們來看一些代碼片斷。首選構建一個總數 32 個數據項的表格模型,而後將其設置爲一個表格視圖的數據:字符串

QTableWidget tableWidget(8, 4);

QItemSelectionModel *selectionModel = tableWidget.selectionModel();

在代碼的最後,咱們得到QTableView的選擇模型,以備之後使用。如今,咱們沒有修改模型中的數據,而是選擇表格左上角的一些單元格。下面咱們來看看代碼如何實現:get

QModelIndex topLeft = tableWidget.model()->index(0, 0, QModelIndex());
QModelIndex bottomRight = tableWidget.model()->index(5, 2, QModelIndex());

接下來,咱們將得到的兩個索引定義爲選區。爲達這一目的,咱們首先構造一個QItemSelection對象,而後將其賦值給咱們獲取的選擇模型:同步

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);

正如前面咱們說的,首先利用左上角和右下角的座標構建一個QItemSelection對象,而後將這個對象設置爲選擇模型的選擇區。select()函數的第一個參數就是須要選擇的選區,第二個參數是選區的標誌位。Qt 提供了不少不一樣的操做,能夠參考下QItemSelectionModel::SelectionFlags的文檔。在本例中,咱們使用了QItemSelectionModel::Select,這意味着選區中所包含的全部單元格都會被選擇。

下面就是咱們的運行結果:

如今咱們知道如何設置選區。下面來看看如何獲取選區。獲取選區須要使用selectedIndexes()函數。該函數返回一個無序列表。咱們能夠經過遍歷這個列表得到哪些被選擇:

QModelIndexList indexes = selectionModel->selectedIndexes();
QModelIndex index;

foreach(index, indexes) {
    QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
    model->setData(index, text);
}

在選擇發生更改時,選擇模型會發出信號。咱們能夠鏈接selectionChanged()信號,在選區改變時檢查哪一個項目發生了變化。這個信號有兩個參數:第一個是新選擇的項目,第二個是剛剛被取消選擇的項目。在下面的示例中,咱們經過selectionChanged()信號,將全部新選擇的項目填充字符串,將全部被取消選擇的部分清空:

void MainWindow::updateSelection(const QItemSelection &selected,
                                 const QItemSelection &deselected)
{
    QModelIndex index;
    QModelIndexList items = selected.indexes();

    foreach (index, items) {
        QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
        model->setData(index, text);
    }

    items = deselected.indexes();

    foreach (index, items) {
        model->setData(index, "");
    }
 }

經過currentChanged(),咱們能夠追蹤當前有焦點的項。同selectionChanged()信號相似,這個信號也有兩個參數:第一個是新的當前項,第二個是上一個當前項。下面的代碼則是該信號的使用:

void MainWindow::changeCurrent(const QModelIndex &current,
                               const QModelIndex &previous)
{
    statusBar()->showMessage(
        tr("Moved from (%1,%2) to (%3,%4)")
            .arg(previous.row()).arg(previous.column())
            .arg(current.row()).arg(current.column()));
}

這些信號能夠用來監控選區的改變。若是你還要直接更新選區,咱們還有另外的方法。

一樣是利用前面所說的QItemSelectionModel::SelectionFlag,咱們能夠對選區進行組合操做。還記得咱們在前面的select()函數中使用過的第二個參數嗎?當咱們替換這個參數,就能夠得到不一樣的組合方式。最經常使用的就是QItemSelectionModel::Select,它的做用是將全部指定的選區都選擇上。QItemSelectionModel::Toggle則是一種取反的操做:若是指定的部分原來已經被選擇,則取消選擇,不然則選擇上。QItemSelectionModel::Deselect則是取消指定的已選擇的部分。在下面的例子中,咱們使用QItemSelectionModel::Toggle對前面的示例做進一步的操做:

QItemSelection toggleSelection;

topLeft = tableWidget.model()->index(2, 1, QModelIndex());
bottomRight = tableWidget.model()->index(7, 3, QModelIndex());
toggleSelection.select(topLeft, bottomRight);

selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);

運行結果將以下所示:

默認狀況下,選擇操做會只會影響到指定的模型索引。可是,咱們也能夠改變這一設置。例如,只選擇整行或者整列:

QItemSelection columnSelection;

topLeft = model->index(0, 1, QModelIndex());
bottomRight = model->index(0, 2, QModelIndex());

columnSelection.select(topLeft, bottomRight);

selectionModel->select(columnSelection,
                       QItemSelectionModel::Select | QItemSelectionModel::Columns);

QItemSelection rowSelection;

topLeft = model->index(0, 0, QModelIndex());
bottomRight = model->index(1, 0, QModelIndex());

rowSelection.select(topLeft, bottomRight);

selectionModel->select(rowSelection,
                       QItemSelectionModel::Select | QItemSelectionModel::Rows);

上面的代碼,咱們依然使用兩個索引設置了一個區域,可是,在選擇的使用咱們使用了QItemSelectionModel::RowsQItemSelectionModel::Columns這兩個參數,所以只會選擇這兩個區域中指定的行或者列:

使用QItemSelectionModel::Current參數能夠將當前選區替換爲新的選區;使用QItemSelectionModel::Clear則會將原來已有的選區所有取消。爲了進行全選,咱們能夠設置選區爲左上角和右下角兩個索引:

QModelIndex topLeft = model->index(0, 0, parent);
QModelIndex bottomRight = model->index(model->rowCount(parent)-1,
model->columnCount(parent)-1, parent);

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);
相關文章
相關標籤/搜索