1. 程式人生 > >QT中view/model中只檢視選擇模型QItemSelectionModel

QT中view/model中只檢視選擇模型QItemSelectionModel

選擇是檢視中常用的一個操作。在列表、樹或者表格中,通過滑鼠點選可以選中某一項,被選中項會變成高亮或者反色。在 Qt 中,選擇也是使用了一種模型。在 model/view 架構中,這種選擇模型提供了一種更通用的對選擇操作的描述。對於一般應用而言,Qt 內建的選擇模型已經足夠,但是,Qt 還是允許你建立自己的選擇模型,來實現一些特殊的操作。

 

Qt 使用QItemSelectionModel類獲取檢視中專案的選擇情況。這個模型保持有專案的索引,並且獨立於任何檢視。這意味著,我們可以讓不同的檢視共享同一個選擇模型,從來達到一種同步操作的目的。選擇由選擇區域組成。模型只將選區的開始和結束的索引位置記錄下來,以保證對於很大的選區也有很好的效能。非連續選區則由多個連續選擇組成。

選擇會直接應用於選擇模型所維護的那些被選中的索引上面。最新的選擇就是當前選擇。這意味著,即便介面上沒有顯示有任何專案被選擇,如果通過某些命令對選區進行操作,同樣會有作用。

在檢視中,始終存在一個當前項和被選擇項(即便從介面上看不到有任何選擇)。與通常所想的不同,當前項和選擇項是相互獨立的兩個狀態。一個專案可以即是當前項又是選擇項。下表是當前項和選擇項的區別:當前項

當前項 選擇項
只能有一個當前項。 可以有多個選擇項。
使用鍵盤或者滑鼠點選可以改變當前項。 選擇項使用兩種狀態:選擇和未選擇,這取決於專案之前的狀態和其它一些設定,例如,單選或多選。只有在使用者進行互動的時候,這種狀態才會發生改變。
當前項可以使用 F2 或者滑鼠雙擊進行編輯(前提是程式允許)。 當前項可以結合另外一個錨點指定被選擇或者去除選擇的一塊選區(或二者的結合)。
當前項通常會有一個焦點框進行標識。 選擇項使用選區顏色進行標識。

在處理選擇的時候,我們可以將QItemSelectionModel當成資料模型中所有資料項的選擇狀態的一個記錄。一旦選擇模型建立好,這些資料項就可以在不知道哪些項被選擇的情況下進行選擇、取消選擇或者改變選擇狀態的操作。所有被選擇項的索引都在可隨時更改,其它元件也可以通過訊號槽機制修改這些選擇的資訊。

標準檢視類(QListViewQTreeView以及QTableView)已經提供了預設的選擇模型,足以滿足大多數應用程式的需求。某一個檢視的選擇模型可以通過selectionModel()函式獲取,然後使用setSelectionModel()提供給其它檢視共享,因此,一般沒有必要新建選擇模型。

如果需要建立一個選區,我們需要指定一個模型以及一對索引,使用這些資料建立一個QItemSelection物件。這兩個索引應該指向給定的模型中的資料,並且作為一個塊狀選區的左上角和右下角的索引。為了將選區應用到模型上,需要將選區提交到選擇模型。這種操作有多種實現,對於現有選擇模型有著不同的影響。

下面我們來看一些程式碼片段。首選構建一個總數 24個數據項的表格模型,然後將其設定為一個表格檢視的資料:

 tableWidget = new QTableWidget(6,4,nullptr);
    tableWidget->setColumnCount(4);
    tableWidget->setRowCount(6);
    selectModel = tableWidget->selectionModel();

QStringList headers,headColunm;
    headers << "ID" << "Name" << "屌長" << "Sex";
    tableWidget->setHorizontalHeaderLabels(headers);
    headColunm <<"一"<<"二"<<"三"<<"四"<<"五"<<"六";
    tableWidget->setVerticalHeaderLabels(headColunm);
    tableWidget->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
    tableWidget->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    tableWidget->setLayoutDirection(Qt::LayoutDirectionAuto);
    tableWidget->setColumnWidth(3,50);
    tableWidget->setColumnWidth(2,50);
//    tableWidget->setFixedHeight(207);
//    tableWidget->setFixedWidth(407);

    tableWidget->setItem(0, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0001")));
    tableWidget->setItem(1, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0002")));
    tableWidget->setItem(2, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0003")));
    tableWidget->setItem(3, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0004")));
    tableWidget->setItem(4, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0005")));
    tableWidget->setItem(5, 0, new QTableWidgetItem(QIcon( QApplication::applicationDirPath()+"/6.jpg"),QString("0005")));

    tableWidget->setItem(0, 1, new QTableWidgetItem(QString("逼哥")));
    tableWidget->setItem(1, 1, new QTableWidgetItem(QString("呂德朋")));
    tableWidget->setItem(2, 1, new QTableWidgetItem(QString("雪山飛舞")));
    tableWidget->setItem(3, 1, new QTableWidgetItem(QString("黑寡婦")));
    tableWidget->setItem(4, 1, new QTableWidgetItem(QString("神州大地")));
    tableWidget->setItem(5, 1, new QTableWidgetItem(QString("血液風閥")));

    tableWidget->setItem(0, 2, new QTableWidgetItem(QString("9mm")));
    tableWidget->setItem(1, 2, new QTableWidgetItem(QString("18cm")));
    tableWidget->setItem(2, 2, new QTableWidgetItem(QString("9")));
    tableWidget->setItem(3, 2, new QTableWidgetItem(QString("9")));
    tableWidget->setItem(4, 2, new QTableWidgetItem(QString("9")));
    tableWidget->setItem(5, 2, new QTableWidgetItem(QString("9")));

    tableWidget->setItem(0, 3, new QTableWidgetItem(QString("男")));
    tableWidget->setItem(1, 3, new QTableWidgetItem(QString("男")));
    tableWidget->setItem(2, 3, new QTableWidgetItem(QString("男")));
    tableWidget->setItem(3, 3, new QTableWidgetItem(QString("女")));
    tableWidget->setItem(4, 3, new QTableWidgetItem(QString("男")));
    tableWidget->setItem(5, 3, new QTableWidgetItem(QString("女")));
    tableWidget->setWindowTitle("檢視 widget");
    tableWidget->show();


    connect(selectModel, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
                      this,  SLOT(changeCurrent(const QModelIndex &,const QModelIndex &)));

    int h = tableWidget->height();
    int w = tableWidget->width();
    int columnWidth = tableWidget->columnWidth(2);
    Q_UNUSED(columnWidth);
    qDebug()<<"h w"<<h<<w;

正如前面我們說的,首先利用左上角和右下角的座標構建一個QItemSelection物件,然後將這個物件設定為選擇模型的選擇區。select()函式的第一個引數就是需要選擇的選區,第二個引數是選區的標誌位。Qt 提供了很多不同的操作,可以參考下QItemSelectionModel::SelectionFlags的文件。在本例中,我們使用了QItemSelectionModel::Select,這意味著選區中所包含的所有單元格都會被選擇。

QModelIndex topLeft = tableWidget->model()->index(1,0,QModelIndex());
    QModelIndex topRight = tableWidget->model()->index(3,3,QModelIndex());
    QItemSelection seletion;
    seletion.select(topLeft,topRight);
    selectModel->select(seletion,QItemSelectionModel::Select);

現在我們知道如何設定選區。下面來看看如何獲取選區。獲取選區需要使用selectedIndexes()函式。該函式返回一個無序列表。我們可以通過遍歷這個列表獲得哪些被選擇:

QDialog *dialog = new QDialog(nullptr,Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint );
    dialog->setWindowTitle(tr("Hello, dialog!"));
    dialog->setAttribute(Qt::WA_DeleteOnClose);
    dialog->open();


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

    QTableWidget* tableView = new QTableWidget(5,4,dialog);
    tableView->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
    tableView->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);//資料內容最大化展示

    tableView->show();
    //tableView->setModel(model);
    foreach(index, indexes)
    {
        //QString text = QString("(%1,%2)").arg(index.row()).arg(index.column()); //顯示位置
        QString text = QString("%1").arg(index.data().toString());
        tableView->model()->setData(index, text,Qt::DisplayRole);
    }

我們上一個model中選擇檢視,放在兩一個檢視的相同模型展示出來

在選擇發生更改時,選擇模型會發出訊號。我們可以連線selectionChanged()訊號,在選區改變時檢查哪個專案發生了變化。這個訊號有兩個引數:第一個是新選擇的專案,第二個是剛剛被取消選擇的專案。在下面的示例中,我們通過selectionChanged()訊號,將所有新選擇的專案填充字串,將所有被取消選擇的部分清空:

void myDefineEvent::changeCurrent(const QModelIndex &current,
                               const QModelIndex &previous)
{
    qDebug()<<"changeCurrent ";
    QStatusBar  *statusBar = new QStatusBar(nullptr);
    statusBar->showMessage(
        tr("Moved from (%1,%2) to (%3,%4)")
            .arg(previous.row()).arg(previous.column())
            .arg(current.row()).arg(current.column()),5);
    qDebug()<<QString(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則是取消指定的已選擇的部分。

原始碼請打擊這裡  原始碼