Qt's Model/View structure

Model/View structure

Separating the interface components from the edited data and connecting them through data sources is a better way to deal with the interface and data. Qt uses the Model/View structure to handle this relationship. The basic structure of Model/View is shown in Figure 5-1. The functions of each part are as follows.

Insert image description here
Figure 1 Basic structure of Model/View (from Qt help file)

What others have written is particularly troublesome, and they don’t know what they mean. Please explain it simply. After using the Model/View structure, when I modify the data on the interface, I modify the data in my Model simultaneously, that is, the data between the two are synchronized. If we do not use the Model/View structure, then after we modify the data of the components on the interface, only the data in my component is modified, but the original data I gave to the component is not modified, so we still have to traverse the component. Data needs to be processed synchronously. The advantage of using this structure is that modifying data means modifying synchronized data.

In the Model/View structure, a delegate function is also provided. The delegate function allows users to customize the interface display and editing method of data. In a standard view component, the delegate function displays a data. When the data is edited, the delegate communicates with the data model through the model index and provides an editor for editing the data, usually a QLineEdit component.

Models, views, and delegates communicate using signals and slots. When the source data changes, the data model emits a signal to notify the view component; when the user operates data on the interface, the view component emits a signal to represent the operation information; when editing the data, the agent emits a signal to inform the data model and the view component of the editor. state.

Know what Model/View is used for. The principles we need to understand during use.

1. Basic structure of data model

In the Model/View structure, the data model provides a standard interface for view components and agents to access data. In Qt, all data model classes inherit from QAbstractItemModel. Regardless of how the underlying data structure organizes data, subclasses of QAbstractItemModel represent data in a table hierarchy. View components use this rule to access the model. data, but the form presented to users is different.

Graphs are three common representations of data models. No matter how the data model is expressed, the basic unit for storing data in the data model is an item. Each item has a row number, a column number, and a parent item. In list and table modes, all items have the same top-level item (root item); in the tree structure, the row number, column number, and parent item are a little more complicated, but these three parameters can completely define a The location of the item to access the item's data.

..\18-0113 Figure\0504.tif
Several manifestations of the graph data model (from Qt help file)

2. model index

In order to ensure that data representation is isolated from data access methods, the concept of model index (model index) is introduced in the data model. Each data accessed through the data model has a model index, and both view components and agents obtain data through the model index.

QModelIndex represents the class of model index. The model index provides a temporary pointer for data access, used to extract or modify data through the data model. Because the structure of the model's internal organization of data may change at any time, model indexes are temporary. If you need to use a persistent model index, use the QPersistentModelIndex class.

3.Row and column numbers

The basic form of the data model is tabular data defined with rows and columns, but this does not mean that the underlying data is stored in a two-dimensional array. The use of rows and columns is just a provision for convenient interaction between components. Data can be accessed through the row number and column number of the model index.

To obtain a model index, three parameters must be provided: row number, column number, and parent model index. For example, for the three data items A, B, and C in the tabular data model as shown in Figure 5-4, the code to obtain the model index is:

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

In the function that creates the model index, you need to pass the row number, column number, and model index of the parent. For list and table mode data models, the top-level node is always represented by QModelIndex().

4. parent

When the data model is a list or table, it is more intuitive to use row numbers and column numbers to store data. The parent item of all data items is the top-level item; when the data model is a tree structure, the situation is more complicated (tree structure , items are generally called nodes), a node can have a parent node, or be the parent node of other nodes. When constructing the model index of a data item, the correct row number, column number, and parent node must be specified.

For the tree data model in Figure 5-4, the parent nodes of node A and node C are top-level nodes, and the code to obtain the model index is:

QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

However, the parent node of node B is node A, and the model index of node B is generated by the following code:

QModelIndex indexB = model->index(1, 0, indexA);

5. role of item

When setting data for an item in the data model, you can assign it data with different item roles. For example, the item data class of the data model class QStandardItemModel is QStandardItem, and its function for setting data is:

void QStandardItem::setData(const QVariant &value,int role= Qt::UserRole + 1)

Among them, value is the data that needs to be set, and role is the role that sets the data. An item can have data with different roles and be used in different situations.

Role is a Qt::ItemDataRole enumeration type, with multiple values. For example, Qt::DisplayRole role is a string displayed in the view component, Qt::ToolTipRole is a mouse prompt message, and Qt::UserRole can customize data. The standard role for items is Qt::DisplayRole.

When obtaining data for an item, you also need to specify a role to obtain data for different roles.

QVariant QStandardItem::data(int role = Qt::UserRole + 1) const

Defining data for the different roles of an item tells the view and delegate components how to display the data. For example, in Figure 5-5, the DisplayRole data of the item is the displayed string, the DecorationRole is the attribute used to decorate the display, and the ToolTipRole defines the mouse tip information. Different view components may interpret and display various role data differently, and may also ignore the data of certain roles.

Picture 134
Figure representation of different role data (from Qt help file)

Use Qt to give us the prepared model model class

Qt has already written some model classes for us. We can combine it with the component View to achieve a synchronized result between the data in the model and the data in the component View.
General steps for using Model/View:
1. First create the xxxxModel class I need.
2. Set initialization for xxxModel, such as adding data, setting directories, etc.
3. Set the model for the View component that matches the Model. setModel() function.
4. Modifying the data in the model is equivalent to modifying the data in the interface. Modifying the data in the interface also modifies the data in the model.

1.QFileSystemModel

Basic functions of the QFileSystemModel class

QFileSystemModel provides a data model that can be used to access the native file system. QFileSystemModel is used in combination with the view component QTreeView to display the file system on the local machine in the form of a directory tree, just like Widnows' resource manager. Using the interface functions provided by QFileSystemModel, you can create a directory, delete a directory, rename a directory, obtain parameters such as file name, directory name, file size, and obtain detailed information about the file.

To obtain the local file system through QFileSystemModel, you need to use the setRootPath() function to set a root directory for QFileSystemModel, for example:

QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());

The static function QDir::currentPath() gets the current path of the application.

The data model class used to obtain the disk file directory also has a QDirModel. The function of QDirModel is similar to that of QFileSystemModel. It can also obtain directories and files, but QFileSystemModel uses a separate thread to obtain the directory file structure, while QDirModel does not use a separate thread. Using a separate thread will not block the main thread, so it is recommended to use QFileSystemModel.

Use of QFileSystemModel

The main window of example samp5_1 is based on QMainWindow, and the toolbar and status bar are deleted when using the UI designer for visual design. The main window interface layout adopts the design of two split bars. ListView and TableView adopt an upper and lower split layout, and then use a horizontal split layout with the TreeView on the left. The horizontal split layout is then laid out horizontally in the main window working area with the groupBox below that displays information.

A member variable model of the QFileSystemModel class is defined in the main window class.
QFileSystemModel *model;
the main window constructor is initialized, the code is as follows:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    
    
   ui->setupUi(this);
   model=new QFileSystemModel(this); 
   model->setRootPath(QDir::currentPath()); //设置根目录
   ui->treeView->setModel(model); //设置数据模型
   ui->listView->setModel(model); //设置数据模型
   ui->tableView->setModel(model); //设置数据模型
//信号与槽关联,treeView单击时,其目录设置为listView和tableView的根节点
   connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
         ui->listView,SLOT(setRootIndex(QModelIndex)));
   connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
         ui->tableView,SLOT(setRootIndex(QModelIndex)));
}

All three view components use the setModel() function to set the QFileSystemModel data model model as their own data model.

The connect() function sets the association between the signal and the slot. The function achieved is: when a node of treeView is clicked, this node is set as the root node of listView and tableView, because the clicked(QModelIndex) signal of treeView will pass a QModelIndex variable , is the model index of the current node. Pass this model index to the slot function setRootIndex(QModelIndex) of listView and tableView, and listView and tableView will display the directories and files under this node.

When you click a node on the treeView, some information about the node will be displayed in some labels below. This is implemented by writing a slot function for the clicked(const QModelIndex &index) signal of the treeView. The code is as follows:

void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
    
    
   ui->chkIsDir->setChecked(model->isDir(index)); //是否是目录
   ui->LabPath->setText(model->filePath(index));
   ui->LabType->setText(model->type(index));
   ui->LabFileName->setText(model->fileName(index));
   int sz=model->size(index)/1024;
   if (sz<1024)
      ui->LabFileSize->setText(QString("%1 KB").arg(sz));
   else
      ui->LabFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}

The function has a passing parameter QModelIndex &index, which is the index of the clicked node in the data model. Through the passed model index index, this code uses some functions of QFileSystemModel to obtain some parameters of the node, including the following.

bool isDir(QModelIndex &index): Determine whether the node is a directory.
QString filePath(QModelIndex &index): Returns the directory name or file name with path of the node.
QString fileName(QModelIndex &index): Returns the folder name or file name with the path removed.
QString type(QModelIndex &index): Returns text describing the node type. For example, the hard disk symbol is "Drive", the folder is "File Folder", and the file is described with a specific suffix, such as "txt File", "exe File", "pdf File" "wait.
qint64 size(QModelIndex &index): If the node is a file, returns the number of bytes of the file size: if the node is a folder, returns 0.
How QFileSystemModel obtains the disk directory file structure and how the three view components display these data are issues of its underlying implementation.

QStringListModel

This model is to better display the contents of ListView and to better match ListView.

1.QStringListModel function overview

QStringListModel is used to process the data model of string lists. It can be used as the data model of QListView to display and edit string lists on the interface.

The setStringList() function of QStringListModel can initialize the contents of the string list of the data model. The stringList() function returns the string list in the data model. After editing and modifying the data in the associated ListView component, the data will be updated in time in the data model. in the string list.

QStringListModel provides functions for editing and modifying string list data, such as insertRows(), removeRows(), setData(), etc. These operations directly affect the string list inside the data model, and the modified data will automatically be displayed in the associated ListView component. Refresh the display.

2.Usage of QStringListModel

1. Model/View structure object and component initialization

The window is a Widget class inherited from QWidget, and the interface adopts visual design. Define a variable of the QStringListModel class in the Widget class:

QStringListModel   *theModel;

Create variables in the constructor of the Widget class and complete the association between the data model and the interface view component. The following is the code of the Widget class constructor:

Widget::Widget(QWidget *parent) :   QWidget(parent),   ui(new Ui::Widget)
{
    
    
   ui->setupUi(this);
   QStringList   theStrList; 
   theStrList<<"北京"<<"上海"<<"天津"<<"河北"<<"山东"<<"四川"<<"重庆";
   theModel=new QStringListModel(this);
   theModel->setStringList(theStrList); //导入theStrList的内容
   ui->listView->setModel(theModel); //设置数据模型
   ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked |
              QAbstractItemView::SelectedClicked);
}

The setStringList() function of QStringListModel uses the contents of a string list as the initial data content of the data model.
QListView's setModel() function sets a data model for the interface view component.
After the program runs, the contents of the initialized string list will be displayed in the ListView component on the interface.

2. Edit, add, and delete items

The edit item
QListView::setEditTriggers() function sets whether the QListView items can be edited and how to enter the editing state. The parameters of the function are a combination of QAbstractItemView::EditTrigger enumeration type values. The constructor is set to:

ui->listView->setEditTriggers(QAbstractItemView::DoubleClicked | 
                  QAbstractItemView::SelectedClicked);

It means that after double-clicking or selecting and clicking a list item, it enters the editing state.

To make it non-editable, you can set it to:

ui->listView->setEditTriggers(QAbstractItemView:: NoEditTriggers);

Adding an item
Adding an item means adding a row at the end of the list. The slot function code of the "Add item" button on the interface is as follows:

void Widget::on_btnListAppend_clicked()
{
    
     //添加一行
   theModel->insertRow(theModel->rowCount()); //在尾部插入一空行
   QModelIndex index=theModel->index(theModel->rowCount()-1,0);
   theModel->setData(index,"new item",Qt::DisplayRole);
   ui->listView->setCurrentIndex(index); //设置当前选中的行
}

Operations on data are all based on the data model. Therefore, inserting a row uses the insertRow (int row) function of QStringListModel, where row is a row number, indicating that a row is inserted before the row row. To insert a row at the end of the list, set the row parameter to the current number of rows in the list.

This just adds a blank line without any text at the end of the list. In order to set a default text title for the added item, you must first obtain the model index of the new item, that is:

QModelIndex  index=theModel->index(theModel->rowCount()-1,0);

The index() function of QStringListModel generates a model index based on the passed row number, column number, and model index of the parent item. This line of code generates a model index index for the last newly added item.

Set a text title "new item" for the new item, use the setData() function, and use the model index index generated previously. code show as below:

theModel->setData(index,"new item",Qt::DisplayRole);

When using the setData() function, you must specify the role of the set data. The role here is Qt::DisplayRole, which is the role used for display, that is, the text title of the item.

Insert item
The function of the "Insert item" button is to insert a row before the current row in the list. The implementation code is as follows:

void Widget::on_btnListInsert_clicked()
{
    
    //插入一行
   QModelIndex  index=ui->listView->currentIndex();
   theModel->insertRow(index.row());  
   theModel->setData(index,"inserted item",Qt::DisplayRole); 
   ui->listView->setCurrentIndex(index);
}

QListView::currentIndex() obtains the model index index of the current item, and index.row() returns the row number of this model index.

Delete the current item.
The code to delete a certain row using the removeRow() function of QStringListModel is as follows:

void Widget::on_btnListDelete_clicked()
{
    
    //删除当前行
   QModelIndex   index=ui->listView->currentIndex(); 
   theModel->removeRow(index.row());  
}

Deleting a list
To delete all items in a list, you can use QStringListModel's removeRows(int row, int count) function, which means deleting count rows starting from row number row. code show as below:

void Widget::on_btnListClear_clicked()
{
    
    //清除所有项
   theModel->removeRows(0,theModel->rowCount());
}

3.Display the contents of the data model as text

When editing items in the ListView on the interface, the actual operations are on its associated data model theModel. After inserting, adding, and deleting items on the data model, the content is immediately displayed on the ListView. This is the data model. The role of signals and slots between view components is to notify the view components to update the display when the content of the data model changes.

Similarly, when you double-click a row on the ListView to enter the editing state and modify the text content of an item, this part of the content is also saved in the data model.

Then, the latest data content should be stored inside the data model. For the QStringListModel model, the latest data copy can be obtained through the stringList() function. The "Show StringList of Data Model" button on the interface obtains the stringList of the data model and displays its content in the form of multi-line text to verify the data modification to the data model. Especially after modifying the text of the list item on the interface, its internal Whether the data is updated synchronously.

The following is the slot function code of the clicked() signal of the "Display StringList of Data Model" button on the interface. It obtains the string list through the stringList() function of the data model and displays it line by line in plainTextEdit:

void Widget::on_btnTextImport_clicked()
{
    
    //显示数据模型的StringList
   QStringList  tmpList=theModel->stringList();
   ui->plainTextEdit->clear(); 
   for (int i=0; i<tmpList.count();i++)
      ui->plainTextEdit->appendPlainText(tmpList.at(i)); 
}

When the program is running, no matter what edits and modifications are made to the ListView list, click the "Show StringList of Data Model" button, the text content displayed in the text box is always exactly the same as that in the ListView, indicating that the data of the data model is consistent with The content displayed on the interface is synchronized.

QStandardItemModel

This model is a model that matches the data in the table.

1. Function overview

QStandardItemModel is a standard data model class based on item data. It is usually combined with QTableView to form a Model/View structure to implement general two-dimensional data management functions.

This section introduces the use of QStandardItemModel, mainly using the following three classes.

QStandardItemModel: A standard data model based on item data that can handle two-dimensional data. Maintain a two-dimensional array of item data. Each item is a variable of the QStandardItem class, used to store the item's data, font format, alignment, etc.
QTableView: Two-dimensional data table view component, with multiple rows and columns. Each basic display unit is a cell. After setting a data model of the QStandardItemModel class through the setModel() function, a cell displays the QStandardItemModel data model. an item.
QItemSelectionModel: A class used to track the cell selection status of the view component. When a cell, or multiple cells, are selected in QTableView, the model index of the selected cell can be obtained through QItemSelectionModel, which provides information for the cell selection operation. convenient.
The relationship between these classes is: QTableView is an interface view component, its associated data model is QStandardItem Model, its associated item selection model is QItemSelectionModel, and the basic unit of data management of QStandardItemModel is QStandardItem.

Open a plain text file, which is a regular two-dimensional data file. Obtain the header and data of each row and column through string processing, and import it into a QStandardItemModel data model.
To edit and modify the data of the data model, you can insert rows, add rows, delete rows, and you can also directly modify the data content of cells in the QTableView view component.
You can set the data of different roles for an item in the data model, including text alignment, whether the font is bold, etc.
Get the current cell on the view component through QItemSelectionModel, as well as the range of selected cells, and operate on the selected cells.
Display the data content of the data model into the QPlainTextEdit component, display the content of the data model, and check whether the modifications made on the view component are synchronized with the data model.
Save the modified model data as a text file.

2. Interface design and main window class definition

The new definition in the main window class MainWindow is as follows (the declaration of the slot function of the interface component generated by the UI designer is omitted):

#define    FixedColumnCount   6      //文件固定6列
class MainWindow : public QMainWindow
{
    
    
   Q_OBJECT
private:
   QLabel  *LabCurFile;              //当前文件
   QLabel  *LabCellPos;              //当前单元格行列号
   QLabel  *LabCellText;             //当前单元格内容
   QStandardItemModel  *theModel;    //数据模型
   QItemSelectionModel *theSelection;//选择模型
   void   iniModelFromStringList(QStringList&);//从StringList初始化数据模型
public:
   explicit MainWindow(QWidget *parent = 0);
   ~MainWindow();
private slots:
//当前选择单元格发生变化
   void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);
private:
   Ui::MainWindow *ui;
};

The data model variable theModel and the item data selection model variable theSelection are defined here.

The private function iniModelFromStringList() is defined to create a data model from the contents of a QStringList variable when the file is opened.

The custom slot function on_currentChanged() is used to update the status bar information display when the selected cell changes on the TableView. This slot function will be associated with the currentChanged() signal of the item selection model theSelection.

3.Usage of QStandardItemModel

1. system initialization

In the constructor of MainWindow, perform interface initialization, creation of data model and selection model, and settings such as association with view components, association of signals and slots, etc. The code is as follows:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    
    
   ui->setupUi(this);
   setCentralWidget(ui->splitter); 
   theModel = new QStandardItemModel(2,FixedColumnCount,this); //数据模型
   theSelection = new QItemSelectionModel(theModel);//选择模型
   connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
         this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
   ui->tableView->setModel(theModel); //设置数据模型
   ui->tableView->setSelectionModel(theSelection);//设置选择模型
   ui->tableView->setSelectionMode(QAbstractItemView::ExtendedSelection);
   ui->tableView->setSelectionBehavior(QAbstractItemView::SelectItems);
//创建状态栏组件,代码略
}

In the constructor, first create the data model theModel. When creating the data selection model, you need to pass a data model variable as its parameter. In this way, the data selection model theSelection is associated with the data model theModel and is used to represent the item data selection operation of theModel.

After creating the data model and selection model, set the data model and selection model for the TableView component:

ui->tableView->setModel(theModel); //设置数据模型
ui->tableView->setSelectionModel(theSelection);//设置选择模型

The constructor also associates the custom slot function on_currentChanged() with the currentChanged() signal of theSelection, which is used to display the row number, column number, content and other information of the cell when the tableView selected cell on the interface changes. The slot function code show as below:

void MainWindow::on_currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    
     //选择单元格变化时的响应
   if (current.isValid()) 
   {
    
    
      LabCellPos->setText(QString::asprintf("当前单元格:%d行,%d列",
                          current.row(),current.column()));
      QStandardItem*   aItem=theModel->itemFromIndex(current); 
      this->LabCellText->setText("单元格内容:"+aItem->text()); 
      QFont   font=aItem->font(); 
      ui->actFontBold->setChecked(font.bold());  
   }
}
2. Import data from text file

QStandardItemModel is a standard data model based on item data. It manages internal data in a form similar to a two-dimensional array and is suitable for processing tabular data. QTableView is generally used for its display.

The data of QStandardItemModel can be data in memory generated by the program, or it can come from files. For example, in actual data processing, some data are often saved in plain text format. They have a fixed number of columns, and each column is an item of data, which actually constitutes a two-dimensional data table. Figure 5-10 is the content of a plain text file to be opened by this example program. The first line of the file is the text title of the data column, which is equivalent to the header of the data table. The data is then stored in rows, and each column is separated by TAB keys. data.

When you click the "Open File" button on the toolbar, you need to select such a file to import into the data model and display and edit it on the tableView. The data in Figure 5-10 has 6 columns, the 1st column is an integer, the 2nd to 4th columns are floating point numbers, the 5th column is text, the 6th column is a logical variable, "1" means true.

The following is the slot function code for the "Open File" button:

void MainWindow::on_actOpen_triggered()
{
    
     //打开文件
   QString  curPath=QCoreApplication::applicationDirPath();
   QString  aFileName=QFileDialog::getOpenFileName(this,"打开一个文件",
              curPath,"井数据文件(*.txt);;所有文件(*.*)");
   if (aFileName.isEmpty())
      return; 

   QStringList fFileContent; 
   QFile aFile(aFileName);
   if (aFile.open(QIODevice::ReadOnly | QIODevice::Text)) //打开文件
   {
    
    
      QTextStream aStream(&aFile); //用文本流读取文件
      ui->plainTextEdit->clear();
      while (!aStream.atEnd())
      {
    
    
         QString  str=aStream.readLine();
         ui->plainTextEdit->appendPlainText(str); 
         fFileContent.append(str);
      }
      aFile.close();
      this->LabCurFile->setText("当前文件:"+aFileName);//状态栏显示
      iniModelFromStringList(fFileContent);//初始化数据模型
   }
}

This code allows the user to select the data text file that needs to be opened, then open the file in read-only and text format, read its contents line by line, display each line of string to the plainTextEdit on the interface, and add it to a temporary In the variable fFileContent of type QStringList.

Then call the custom function iniModelFromStringList() to initialize the data model with the content of fFileContent. The following is the code of the iniModelFromStringList() function:

``void MainWindow::iniModelFromStringList(QStringList& aFileContent)
{ //Get data from a StringList, initialize the data model
int rowCnt=aFileContent.count(); //Number of text lines, line 1 is the title
theModel->setRowCount(rowCnt- 1);
//Set the header, a string separated by one or more spaces, TAB and other delimiters, decomposed into a StringList
QString header=aFileContent.at(0);//The first line is the header
QStringList headerList=
header.split(QRegExp(“\s+”),QString::SkipEmptyParts);
theModel->setHorizontalHeaderLabels(headerList); //Set header text
//Set table data
QStandardItem *aItem;
QStringList tmpList;
int j;
for (int i=1;i<rowCnt;i++)
{ QString aLineText=aFileContent.at(i); tmpList=aLineText.split(QRegExp(“\s+”),QString::SkipEmptyParts);


for (j=0;j<FixedColumnCount-1;j++)
{ //Does not include the last column
aItem=new QStandardItem(tmpList.at(j));
theModel->setItem(i-1,j,aItem); // Set Item for a certain row and column position of the model
}
aItem=new QStandardItem(headerList.at(j));//The last column
aItem->setCheckable(true); //Set to Checkable
if (tmpList.at(j)== "0")
aItem->setCheckState(Qt::Unchecked);
else
aItem->setCheckState(Qt::Checked);
theModel->setItem(i-1,j,aItem);
}
}`cpp

传递来的参数aFileContent是文本文件所有行构成的StringList,文件的每一行是aFileContent的一行字符串,第1行是表头文字,数据从第2行开始。

程序首先获取字符串列表的行数,然后设置数据模型的行数,因为数据模型的列数在初始化时已经设置了。

然后获取字符串列表的第1行,即表头文字,用QString::split()函数分割成一个QStringList,设置为数据模型的表头标题。

QString::split()函数根据某个特定的符号将字符串进行分割。例如,header是数据列的标题,每个标题之间通过一个或多个TAB键分隔,其内容是:

测深(m)    垂深(m)     方位(°)     总位移(m)     固井质量    测井取样
那么通过上面的split()函数操作,得到一个字符串列表headerList,其内容是:

测深(m)
垂深(m)
方位(°)
总位移(m)
固井质量
测井取样
也就是分解为一个6行的StringList。然后使用此字符串列表作为数据模型,设置表头标题的函数setHorizontalHeaderLabels()的参数,就可以为数据模型设置表头了。

同样,在逐行获取字符串后,也采用split()函数进行分解,为每个数据创建一个QStandardItem类型的项数据aItem,并赋给数据模型作为某行某列的项数据。

QStandardItemModel以二维表格的形式保存项数据,每个项数据对应着QTableView的一个单元格。项数据不仅可以存储显示的文字,还可以存储其他角色的数据。

数据文件的最后一列是一个逻辑型数据,在tableView上显示时为其提供一个CheckBox组件,此功能通过调用QStandardItem的setCheckable()函数实现。

#### 3.数据修改

当TableView设置为可编辑时,双击一个单元格可以修改其内容,对于使用CheckBox的列,改变CheckBox的勾选状态,就可以修改单元格关联项的选择状态。

在实例主窗口工具栏上有“添加行”“插入行”“删除行”按钮,它们实现相应的编辑操作,这些操作都是直接针对数据模型的,数据模型被修改后,会直接在TableView上显示出来。

**添加行**
“添加行”操作是在数据表的最后添加一行,其实现代码如下:

```cpp
void MainWindow::on_actAppend_triggered()
{ //在表格最后添加行
   QList<QStandardItem*>  aItemList; //列表类
   QStandardItem   *aItem;
   for(int i=0;iheaderData(theModel->columnCount()-1, Qt::Horizontal, Qt::DisplayRole).toString();
   aItem=new QStandardItem(str); //创建 "测井取样" Item
   aItem->setCheckable(true);
   aItemList<insertRow(theModel->rowCount(),aItemList); //插入一行
   QModelIndex curIndex=theModel->index(theModel->rowCount()-1,0);
   theSelection->clearSelection();
   theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select); 
}

Use the QStandardItemModel::insertRow() function to insert a row. Its function prototype is:

void insertRow(int row, const QList<QStandardItem *> &items)

Among them, row is a row number, indicating that a row is inserted before this row number. If row is equal to or greater than the total number of rows, a row is added at the end. QList<QStandardItem *> &items is a list class of QStandardItem type. It is necessary to create a QStandardItem type item for each item data of an inserted row, and then pass it to the insertRow() function.

In this program, when creating QStandardItem objects for the first five columns, the text "0" is used, and the last column uses the header title and is set to Checkable. After creating each item data object, use the insertRow() function to add a row at the end.

Insert row
The function of the "Insert row" button is to insert a row in front of the current row. The implementation code is similar to that of "Add row".

Delete row
The function of the "Delete row" button is to delete the current row. First, get the model index of the current cell from the selection model, then get the row number from the model index, and call removeRow(int row) to delete the specified row.

void MainWindow::on_actDelete_triggered()
{
    
     //删除行
   QModelIndex curIndex=theSelection->currentIndex();//获取模型索引
   if (curIndex.row()==theModel->rowCount()-1)//最后一行
      theModel->removeRow(curIndex.row()); //删除最后一行
   else
   {
    
    
     theModel->removeRow(curIndex.row());//删除一行,并重新设置当前选择行
     theSelection->setCurrentIndex(curIndex,QItemSelectionModel::Select);
   }
}

4. Cell formatting

There are three buttons on the toolbar to set the alignment of cell text, and a button to set the bold font. When multiple cells are selected in a TableView, multiple cells can be formatted at the same time. For example, the code for the "Left" button is as follows:

void MainWindow::on_actAlignLeft_triggered()
{
    
    //设置文字居左对齐
   if (!theSelection->hasSelection())
      return;
//获取选择的单元格的模型索引列表,可以是多选
   QModelIndexList selectedIndex=theSelection->selectedIndexes();
   for (int i=0;i<selectedIndex.count();i++)
   {
    
    
      QModelIndex  aIndex=selectedIndex.at(i); //获取一个模型索引
      QStandardItem*  aItem=theModel->itemFromIndex(aIndex);
      aItem->setTextAlignment(Qt::AlignLeft);//设置文字对齐方式
   }
}

The QItemSelectionModel::selectedIndexes() function returns the model index list of the selected cell, then obtains the model index of each selected cell through this list, then obtains its item data through the model index, and then calls QStandardItem::set TextAlignment() to set The alignment of an item is enough.

The code for the Center and Right buttons is similar.

The "Bold" button sets whether the font of the cell is bold. When selecting a cell, the check status of actFontBold is automatically updated based on whether the font of the current cell is bold. The slot function code of actFontBold's triggered(bool) is as follows, which is similar to the code for setting alignment:

void MainWindow::on_actFontBold_triggered(bool checked)
{
    
    //设置字体粗体
   if (!theSelection->hasSelection())
      return;
   QModelIndexList selectedIndex=theSelection->selectedIndexes();
   for (int i=0;i< selectedIndex.count();i++)
   {
    
    
      QModelIndex aIndex= selectedIndex.at(i); //获取一个模型索引
      QStandardItem*   aItem=theModel->itemFromIndex(aIndex);//获取项数据
      QFont  font=aItem->font();
      font.setBold(checked); //设置字体是否粗体
      aItem->setFont(font); 
   }
}
5. Save data as file

Modifications to the data on the view component will be automatically updated to the data model. Click the "Model Data Preview" button on the toolbar to display the data content of the data model into PlainTextEdit.

The data in the data model is in memory. The "Save File" button on the toolbar can save the data in the data model as a data text file and also display it in PlainTextEdit. The implementation code is as follows:

void MainWindow::on_actSave_triggered()
{
    
     //保存为文件
    QString curPath=QCoreApplication::applicationDirPath(); //获取应用程序的路径
//调用打开文件对话框选择一个文件
    QString aFileName=QFileDialog::getSaveFileName(this,tr("选择一个文件"),curPath,
                 "井斜数据文件(*.txt);;所有文件(*.*)");

    if (aFileName.isEmpty()) //未选择文件,退出
        return;

    QFile aFile(aFileName);
    if (!(aFile.open(QIODevice::ReadWrite | QIODevice::Text | QIODevice::Truncate)))
        return; //以读写、覆盖原有内容方式打开文件

    QTextStream aStream(&aFile); //用文本流读取文件

    QStandardItem   *aItem;
    int i,j;
    QString str;

    ui->plainTextEdit->clear();

//获取表头文字
    for (i=0;i<theModel->columnCount();i++)
    {
    
    
        aItem=theModel->horizontalHeaderItem(i); //获取表头的项数据
        str=str+aItem->text()+"\t\t";  //以TAB见隔开
    }
    aStream<<str<<"\n";  //文件里需要加入换行符 \n
    ui->plainTextEdit->appendPlainText(str);

//获取数据区文字
    for ( i=0;i<theModel->rowCount();i++)
    {
    
    
        str="";
        for( j=0;j<theModel->columnCount()-1;j++)
        {
    
    
            aItem=theModel->item(i,j);
            str=str+aItem->text()+QString::asprintf("\t\t");
        }

        aItem=theModel->item(i,j); //最后一列是逻辑型
        if (aItem->checkState()==Qt::Checked)
            str=str+"1";
        else
            str=str+"0";

         ui->plainTextEdit->appendPlainText(str);
         aStream<<str<<"\n";
    }
}

Custom proxy

1. Customize proxy functions

To put it simply, because the components provided by the system's View cannot well match the data content we want, we need to set the content of the components we want ourselves.

The QTableView component provides a default proxy editing component for each cell, which is a QLineEdit component. You can enter any data in the edit box, so it is more versatile. But in some cases, you want to use different editing components according to the type of data. For example, if the data is an integer, it is more appropriate to use QSpinBox as the editing component; the data is floating point numbers, it is more appropriate to use QDoubleSpinBox; for Boolean type data, use a QComboBox, from A set of list text selections is more appropriate.

To implement these functions, you need to set a custom proxy component for a certain column or cell of the TableView. Add custom proxy component function to TableView.

2. Basic design requirements for custom proxy classes

The hierarchical structure of several classes related to agents in Qt is shown in the figure.

..\18-0113 Figure\0512.tifFigure hierarchical structure of classes that implement proxy functionality

QAbstractItemDelegateis for all agent classesabstract base classQStyledItemDelegateIs used by view componentsDefault proxy classQItemDelegateIt is also a class with similar functions. The difference between QStyledItemDelegate and QItemDelegate is that QStyledItemDelegate can use the current style sheet settings to draw components, so it is recommended to use QStyledItem Delegate as the base class of custom proxy components.

Regardless of whether you inherit from QStyledItemDelegate or QItemDelegate to design a custom proxy component, you must implement the following4functions:

The createEditor() function creates a widget component for editing model data, such as a QSpinBox component or a QComboBox component;

The setEditorData() function obtains data from the data model for editing by the widget component;

setModelData() updates the data on the widget to the data model;

updateEditorGeometry() is used to set an appropriate size for the widget component.

3. Custom proxy class based on QSpinBox

1. Basic structure of custom proxy class

Let's design a custom proxy class based on the QSpinBox class, which is used to "reshape" the editing of data columns.

Click the "File" → "New File or Project" menu item in Qt Creator, select a new C++ class file in the "New File or Project" dialog box that appears, and enter your custom file in the dialog box that appears. The name of the class is QWIntSpinDelegate, set the base class to QStyledItemDelegate, click Next to end the wizard, the system will automatically generate header files and source files and add them to the project.

The header file qwintspindelegate.h contains the definition of the custom class QWIntSpinDelegate, and adds the definitions of four functions that need to be redefined. The content of qwintspindelegate.h is as follows:

#include   <QStyledItemDelegate>
class QWIntSpinDelegate : public QStyledItemDelegate
{
    
    
   Q_OBJECT
public:
   QWIntSpinDelegate(QObject *parent=0);
//自定义代理组件必须继承以下4个函数
  QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
        const QModelIndex &index) const Q_DECL_OVERRIDE;
  void setEditorData(QWidget *editor, 
               const QModelIndex &index)const Q_DECL_OVERRIDE;
   void setModelData(QWidget *editor, QAbstractItemModel *model,
                const QModelIndex &index) const Q_DECL_OVERRIDE;
   void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem
       &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
};

Custom proxy components must reimplement these four functions, and the prototypes of the functions are fixed.

2. Implementation of createEditor() function

The createEditor() function is used to create the required editing component. The QWIntSpinDelegate class hopes to create a QSpinBox as an editing component. The function is implemented as follows:

QWidget *QWIntSpinDelegate::createEditor(QWidget *parent,
   const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    
     //创建代理编辑组件
   QSpinBox *editor = new QSpinBox(parent); 
   editor->setFrame(false); //设置为无边框
   editor->setMinimum(0); 
   editor->setMaximum(10000);
   return editor;  //返回此编辑器
}

This code creates an editor of type QSpinBox, with parent pointing to the view component; then it makes some settings for the created editor and uses editor as the return value of the function.

3.setEditorData() function

The setEditorData() function is used to obtain a value from the data model and set it as the editor's display value. When a cell is double-clicked to enter the editing state, this function will be automatically called. Its implementation code is as follows:

void QWIntSpinDelegate::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); 
}

The parameter editor passed by the function points to the agent editing component, and index is the model index of the associated data unit.

Convert the editor to the QSpinBox type component spinBox through forced type conversion, and then set the obtained value to the value of the spinBox.

4. setModelData() function

The setModelData() function is used to update the value on the proxy editor to the data model. When the user completes editing on the interface, this function will be automatically called to update the data on the interface to the data model. The code is as follows:

void QWIntSpinDelegate::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);
}

The program first obtains the value in the agent component editor, and then uses the passed data model model and model index parameter index to update the latest value of the editor into the data model.

5. updateEditorGeometry() function

The updateEditorGeometry() function is used to set an appropriate size for the proxy component. The rect variable of the parameter option passed by the function defines the size of the cell suitable for displaying the proxy component. You can set it directly to this value. code show as below.

void QWIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
    
     //设置组件大小
   editor->setGeometry(option.rect);
}

4. Use of custom proxy classes

Similarly, you can create a custom proxy component class QWFloatSpinDelegate based on QDoubleSpinBox for editing floating point numbers, and you can also create a custom component class QWComboBoxDelegate based on QComboBox. Define instance variables of three proxy classes in the class definition of the main window (other definitions are omitted):

class MainWindow : public QMainWindow
{
    
    
private:
   QWIntSpinDelegate   intSpinDelegate; //整型数
   QWFloatSpinDelegate  floatSpinDelegate; //浮点数
   QWComboBoxDelegate   comboBoxDelegate; //列表选择
}

In the constructor of MainWindow, set custom proxy components for certain columns of the tableView. The constructor code of the custom proxy component is added as follows (some unimportant content such as the initialization status bar is removed):

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    
    
   ui->setupUi(this);
   theModel = new QStandardItemModel(2,FixedColumnCount,this); 
   theSelection = new QItemSelectionModel(theModel);//选择模型
   connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),
         this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));
   ui->tableView->setModel(theModel); //设置数据模型
   ui->tableView->setSelectionModel(theSelection);//设置选择模型
//为各列设置自定义代理组件
   ui->tableView->setItemDelegateForColumn(0,&intSpinDelegate);//测深
   ui->tableView->setItemDelegateForColumn(1,&floatSpinDelegate);//浮点数
   ui->tableView->setItemDelegateForColumn(2,&floatSpinDelegate); //浮点数
   ui->tableView->setItemDelegateForColumn(3,&floatSpinDelegate); //浮点数
   ui->tableView->setItemDelegateForColumn(4,&comboBoxDelegate); //列表
}

To set a custom proxy component for a column of the TableView, use the setItemDelegateForColumn() function; to set a custom proxy component for a row, use the setItemDelegateForRow() function; to set a custom proxy component for the entire TableView, call the setItemDelegate() function .

Guess you like

Origin blog.csdn.net/simple_core/article/details/124633406