1. Qt 模型视图代理
Qt 模型视图代理,也可以称为 MVD 模式
模型(model)、视图(view)、代理(delegate) 主要用来显示编辑数据
1.1 模型
模型 (Model) 是视图与原始数据之间的接口
原始数据可以是:数据库的一个数据表、内存中的一个 StringList,磁盘文件结构 等 QAbstractItemModel 是所有模型的祖宗类,其它 model 类都派生于它
1.2 视图
视图 (View) 是显示和编辑数据的界面组件
主要的视图组件有 QListView、QTreeView 和 QTableView QListWidget、QTreeWidget 和 QTableWidget 是视图类的简化版
它们不使用数据模型,而是将数据直接存储在组件的每个项里 QAbstractItemView 是所有视图的祖宗类,其它 view 类都派生于它
1.3 代理
代理 (Delegate) 为视图组件提供数据编辑器
如在表格组件中,编辑一个单元格的数据时,缺省是使用一个 QLineEdit 编辑框 代理负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后,又将其保存到数据模型中
2. QTableView 应用
2.1 widget.ui
2.2 主窗口
2.2.1 widget.h
# ifndef WIDGET_H
# define WIDGET_H
# include <QWidget>
# include <QStandardItemModel>
# include <QItemSelectionModel>
# include "cintspindelegate.h"
# include "cfloatspindelegate.h"
# include "ccomboboxdelegate.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget ; }
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public :
Widget ( QWidget * parent = nullptr ) ;
~ Widget ( ) ;
private slots:
void on_btnOpenExcel_clicked ( ) ;
void on_btnReshowData_clicked ( ) ;
void OnCurrentChanged ( const QModelIndex & current, const QModelIndex & previous) ;
void on_btnAppendLast_clicked ( ) ;
void on_btnAppend_clicked ( ) ;
void on_btnDeleteSelectedLine_clicked ( ) ;
private :
Ui:: Widget * ui;
QStandardItemModel * m_pItemModel;
QItemSelectionModel * m_pSelectionModel;
CIntSpinDelegate m_intSpinDelegate;
CFloatSpinDelegate m_floatSpinDelegate;
CComboBoxDelegate m_comboBoxDelegate;
} ;
# endif
2.2.2 widget.cpp
# include "widget.h"
# include "ui_widget.h"
# include <QAxObject>
# include <QFileDialog>
# include <QStandardPaths>
static const int COLUMN_COUNT = 7 ;
Widget :: Widget ( QWidget * parent) : QWidget ( parent) , ui ( new Ui:: Widget) {
ui-> setupUi ( this ) ;
showMaximized ( ) ;
m_pItemModel = new QStandardItemModel ( 1 , COLUMN_COUNT, this ) ;
m_pSelectionModel = new QItemSelectionModel ( m_pItemModel) ;
connect ( m_pSelectionModel, & QItemSelectionModel:: currentChanged, this , & Widget:: OnCurrentChanged) ;
ui-> tableView-> setModel ( m_pItemModel) ;
ui-> tableView-> setSelectionModel ( m_pSelectionModel) ;
ui-> tableView-> setSelectionMode ( QAbstractItemView:: ExtendedSelection) ;
ui-> tableView-> setSelectionBehavior ( QAbstractItemView:: SelectItems) ;
ui-> tableView-> setItemDelegateForColumn ( 3 , & m_floatSpinDelegate) ;
ui-> tableView-> setItemDelegateForColumn ( 4 , & m_intSpinDelegate) ;
ui-> tableView-> setItemDelegateForColumn ( 5 , & m_comboBoxDelegate) ;
}
Widget :: ~ Widget ( ) {
delete ui;
}
void Widget :: on_btnOpenExcel_clicked ( ) {
QAxObject * excel = new QAxObject ( this ) ;
excel-> setControl ( "Excel.Application" ) ;
excel-> setProperty ( "Visible" , false ) ;
excel-> setProperty ( "DisplayAlerts" , true ) ;
QAxObject * workbooks = excel-> querySubObject ( "WorkBooks" ) ;
QString str = QFileDialog :: getOpenFileName ( this , u8"打开excel" ,
"D:/MyQtCreatorProject/9_2_tableView" ,
u8"Excel 文件(*.xls *.xlsx)" ) ;
workbooks-> dynamicCall ( "Open(const QString&)" , str) ;
QAxObject * workbook = excel-> querySubObject ( "ActiveWorkBook" ) ;
QAxObject * worksheet = workbook-> querySubObject ( "WorkSheets(int)" , 1 ) ;
QAxObject * usedRange = worksheet-> querySubObject ( "UsedRange" ) ;
QVariant var = usedRange-> dynamicCall ( "Value" ) ;
QList< QList< QVariant>> excel_list;
QVariantList varRows = var. toList ( ) ;
if ( varRows. isEmpty ( ) ) {
return ;
}
const int row_count = varRows. size ( ) ;
QVariantList rowData;
for ( int i = 0 ; i < row_count; ++ i) {
rowData = varRows[ i] . toList ( ) ;
excel_list. push_back ( rowData) ;
}
QList< QStringList> contentList;
for ( int i = 0 ; i < row_count; i++ ) {
QList< QVariant> curList = excel_list. at ( i) ;
int curRowCount = curList. size ( ) ;
QStringList oneLineStrlist;
for ( int j = 0 ; j < curRowCount; j++ ) {
QString content = curList. at ( j) . toString ( ) ;
oneLineStrlist << content;
}
contentList << oneLineStrlist;
}
workbook-> dynamicCall ( "Close(Boolean)" , false ) ;
excel-> dynamicCall ( "Quit(void)" ) ;
delete excel;
int rowCounts = contentList. size ( ) ;
QStandardItem * aItem;
for ( int i = 0 ; i < rowCounts; i++ ) {
QStringList tmpList = contentList[ i] ;
if ( i == 0 ) {
m_pItemModel-> setHorizontalHeaderLabels ( tmpList) ;
} else {
int j;
for ( j = 0 ; j < COLUMN_COUNT - 1 ; j++ ) {
aItem = new QStandardItem ( tmpList. at ( j) ) ;
m_pItemModel-> setItem ( i- 1 , j, aItem) ;
}
aItem = new QStandardItem ( contentList[ 0 ] . at ( j) ) ;
aItem-> setCheckable ( true ) ;
if ( tmpList. at ( j) == "0" )
aItem-> setCheckState ( Qt:: Unchecked) ;
else
aItem-> setCheckState ( Qt:: Checked) ;
m_pItemModel-> setItem ( i- 1 , j, aItem) ;
}
}
}
void Widget :: OnCurrentChanged ( const QModelIndex & current, const QModelIndex & previous) {
Q_UNUSED ( previous) ;
if ( current. isValid ( ) ) {
ui-> textEdit-> clear ( ) ;
ui-> textEdit-> append ( QString :: asprintf ( u8"当前单元格:%d行,%d列" ,
current. row ( ) , current. column ( ) ) ) ;
QStandardItem * aItem;
aItem = m_pItemModel-> itemFromIndex ( current) ;
ui-> textEdit-> append ( u8"单元格内容:" + aItem-> text ( ) ) ;
}
}
void Widget :: on_btnAppendLast_clicked ( ) {
QList< QStandardItem* > aItemList;
QStandardItem * aItem;
for ( int i = 0 ; i < COLUMN_COUNT - 1 ; i++ ) {
aItem = new QStandardItem ( u8"自定义" ) ;
aItemList << aItem;
}
QString str = m_pItemModel-> headerData ( m_pItemModel-> columnCount ( ) - 1 , Qt:: Horizontal, Qt:: DisplayRole) . toString ( ) ;
aItem = new QStandardItem ( str) ;
aItem-> setCheckable ( true ) ;
aItemList<< aItem;
m_pItemModel-> insertRow ( m_pItemModel-> rowCount ( ) , aItemList) ;
QModelIndex curIndex = m_pItemModel-> index ( m_pItemModel-> rowCount ( ) - 1 , 0 ) ;
m_pSelectionModel-> clearSelection ( ) ;
m_pSelectionModel-> setCurrentIndex ( curIndex, QItemSelectionModel:: Select) ;
}
void Widget :: on_btnAppend_clicked ( ) {
QList< QStandardItem* > aItemList;
QStandardItem * aItem;
for ( int i = 0 ; i < COLUMN_COUNT- 1 ; i++ ) {
aItem = new QStandardItem ( u8"自定义" ) ;
aItemList << aItem;
}
QString str = m_pItemModel-> headerData ( m_pItemModel-> columnCount ( ) - 1 , Qt:: Horizontal, Qt:: DisplayRole) . toString ( ) ;
aItem = new QStandardItem ( str) ;
aItem-> setCheckable ( true ) ;
aItemList<< aItem;
QModelIndex curIndex = m_pSelectionModel-> currentIndex ( ) ;
m_pItemModel-> insertRow ( curIndex. row ( ) , aItemList) ;
m_pSelectionModel-> clearSelection ( ) ;
m_pSelectionModel-> setCurrentIndex ( curIndex, QItemSelectionModel:: Select) ;
}
void Widget :: on_btnDeleteSelectedLine_clicked ( ) {
QModelIndex curIndex = m_pSelectionModel-> currentIndex ( ) ;
if ( curIndex. row ( ) == m_pItemModel-> rowCount ( ) - 1 ) {
m_pItemModel-> removeRow ( curIndex. row ( ) ) ;
} else {
m_pItemModel-> removeRow ( curIndex. row ( ) ) ;
m_pSelectionModel-> setCurrentIndex ( curIndex, QItemSelectionModel:: Select) ;
}
}
void Widget :: on_btnReshowData_clicked ( ) {
ui-> textEdit-> clear ( ) ;
QStandardItem * aItem;
QString str;
int i, j;
for ( i = 0 ; i < m_pItemModel-> columnCount ( ) ; i++ ) {
aItem = m_pItemModel-> horizontalHeaderItem ( i) ;
str = str + aItem-> text ( ) + "\t" ;
}
ui-> textEdit-> append ( str) ;
for ( i = 0 ; i < m_pItemModel-> rowCount ( ) ; i++ ) {
str = "" ;
for ( j = 0 ; j< m_pItemModel-> columnCount ( ) - 1 ; j++ ) {
aItem = m_pItemModel-> item ( i, j) ;
str = str + aItem-> text ( ) + QString :: asprintf ( "\t" ) ;
}
aItem = m_pItemModel-> item ( i, j) ;
if ( aItem-> checkState ( ) == Qt:: Checked)
str = str + "1" ;
else
str = str + "0" ;
ui-> textEdit-> append ( str) ;
}
}
2.3 整型数 spinbox 代理
2.3.1 cintspindelegate.h
# ifndef CINTSPINDELEGATE_H
# define CINTSPINDELEGATE_H
# include <QStyledItemDelegate>
class CIntSpinDelegate : public QStyledItemDelegate {
Q_OBJECT
public :
CIntSpinDelegate ( QObject * parent= 0 ) ;
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;
} ;
# endif
2.3.2 cintspindelegate.cpp
# include "cintspindelegate.h"
# include <QSpinBox>
CIntSpinDelegate :: CIntSpinDelegate ( QObject * parent) : QStyledItemDelegate ( parent) { }
QWidget * CIntSpinDelegate :: createEditor ( QWidget * parent,
const QStyleOptionViewItem & option, const QModelIndex & index) const {
Q_UNUSED ( option) ;
Q_UNUSED ( index) ;
QSpinBox * editor = new QSpinBox ( parent) ;
editor-> setFrame ( false ) ;
editor-> setMinimum ( 0 ) ;
editor-> setMaximum ( 120 ) ;
return editor;
}
void CIntSpinDelegate :: 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) ;
}
void CIntSpinDelegate :: 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) ;
}
void CIntSpinDelegate :: updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const {
Q_UNUSED ( index) ;
editor-> setGeometry ( option. rect) ;
}
2.4 浮点数 spinbox 代理
2.4.1 cfloatspindelegate.h
# ifndef CFLOATSPINDELEGATE_H
# define CFLOATSPINDELEGATE_H
# include <QObject>
# include <QWidget>
# include <QStyledItemDelegate>
class CFloatSpinDelegate : public QStyledItemDelegate {
Q_OBJECT
public :
CFloatSpinDelegate ( QObject * parent= 0 ) ;
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;
} ;
# endif
2.4.2 cfloatspindelegate.cpp
# include "cfloatspindelegate.h"
# include <QDoubleSpinBox>
CFloatSpinDelegate :: CFloatSpinDelegate ( QObject * parent) : QStyledItemDelegate ( parent) { }
QWidget * CFloatSpinDelegate :: createEditor ( QWidget * parent,
const QStyleOptionViewItem & option, const QModelIndex & index) const {
Q_UNUSED ( option) ;
Q_UNUSED ( index) ;
QDoubleSpinBox * editor = new QDoubleSpinBox ( parent) ;
editor-> setFrame ( false ) ;
editor-> setMinimum ( 0 ) ;
editor-> setDecimals ( 2 ) ;
editor-> setMaximum ( 100 ) ;
return editor;
}
void CFloatSpinDelegate :: setEditorData ( QWidget * editor, const QModelIndex & index) const {
float value = index. model ( ) -> data ( index, Qt:: EditRole) . toFloat ( ) ;
QDoubleSpinBox * spinBox = static_cast < QDoubleSpinBox* > ( editor) ;
spinBox-> setValue ( value) ;
}
void CFloatSpinDelegate :: setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
QDoubleSpinBox * spinBox = static_cast < QDoubleSpinBox* > ( editor) ;
spinBox-> interpretText ( ) ;
float value = spinBox-> value ( ) ;
QString str = QString :: asprintf ( "%.2f" , value) ;
model-> setData ( index, str, Qt:: EditRole) ;
}
void CFloatSpinDelegate :: updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index) const {
editor-> setGeometry ( option. rect) ;
}
2.5 combobox 代理
2.5.1 ccomboboxdelegate.h
# ifndef CCOMBOBOXDELEGATE_H
# define CCOMBOBOXDELEGATE_H
# include <QItemDelegate>
class CComboBoxDelegate : public QItemDelegate {
Q_OBJECT
public :
CComboBoxDelegate ( QObject * parent= 0 ) ;
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;
} ;
# endif
2.5.2 ccomboboxdelegate.cpp
# include "ccomboboxdelegate.h"
# include <QComboBox>
CComboBoxDelegate :: CComboBoxDelegate ( QObject * parent) : QItemDelegate ( parent) { }
QWidget * CComboBoxDelegate :: createEditor ( QWidget * parent,
const QStyleOptionViewItem & option, const QModelIndex & index) const {
QComboBox * editor = new QComboBox ( parent) ;
editor-> addItem ( u8"优" ) ;
editor-> addItem ( u8"良" ) ;
editor-> addItem ( u8"一般" ) ;
return editor;
}
void CComboBoxDelegate :: setEditorData ( QWidget * editor, const QModelIndex & index) const {
QString str = index. model ( ) -> data ( index, Qt:: EditRole) . toString ( ) ;
QComboBox * comboBox = static_cast < QComboBox* > ( editor) ;
comboBox-> setCurrentText ( str) ;
}
void CComboBoxDelegate :: setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const {
QComboBox * comboBox = static_cast < QComboBox* > ( editor) ;
QString str = comboBox-> currentText ( ) ;
model-> setData ( index, str, Qt:: EditRole) ;
}
void CComboBoxDelegate :: updateEditorGeometry ( QWidget * editor,
const QStyleOptionViewItem & option, const QModelIndex & index) const {
editor-> setGeometry ( option. rect) ;
}
3. QListView 应用
3.1 widget.h
# ifndef WIDGET_H
# define WIDGET_H
# include <QWidget>
# include <QStringListModel>
# include <QMenu>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget ; }
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public :
Widget ( QWidget * parent = nullptr ) ;
~ Widget ( ) ;
private :
void initMenu ( ) ;
private slots:
void on_btnAddItem_clicked ( ) ;
void on_btnDeleteItem_clicked ( ) ;
void on_btnInsert_clicked ( ) ;
void on_btnClearAllData_clicked ( ) ;
void on_btnReshow_clicked ( ) ;
void on_showRightMenu ( const QPoint& pos) ;
void OnActionDelete ( ) ;
void on_listView_clicked ( const QModelIndex & index) ;
private :
Ui:: Widget * ui;
QStringListModel* m_pStringListModel;
QMenu * m_pMenu;
} ;
# endif
3.2 widget.cpp
# include "widget.h"
# include "ui_widget.h"
# include <QMenu>
Widget :: Widget ( QWidget * parent) : QWidget ( parent) , ui ( new Ui:: Widget) {
ui-> setupUi ( this ) ;
this -> setWindowTitle ( u8"QListView使用教程" ) ;
QStringList strList;
strList << u8"北京" << u8"上海" << u8"深圳" << u8"广东"
<< u8"南京" << u8"苏州" << u8"西安" ;
m_pStringListModel = new QStringListModel ( this ) ;
m_pStringListModel-> setStringList ( strList) ;
ui-> listView-> setModel ( m_pStringListModel) ;
initMenu ( ) ;
ui-> listView-> setContextMenuPolicy ( Qt:: CustomContextMenu) ;
connect ( ui-> listView, & QListView:: customContextMenuRequested, this , & Widget:: on_showRightMenu) ;
}
Widget :: ~ Widget ( ) {
delete ui;
}
void Widget :: on_btnAddItem_clicked ( ) {
m_pStringListModel-> insertRow ( m_pStringListModel-> rowCount ( ) ) ;
QModelIndex index = m_pStringListModel-> index ( m_pStringListModel-> rowCount ( ) - 1 , 0 ) ;
m_pStringListModel-> setData ( index, "new item" , Qt:: DisplayRole) ;
ui-> listView-> setCurrentIndex ( index) ;
}
void Widget :: on_btnDeleteItem_clicked ( ) {
QModelIndex index = ui-> listView-> currentIndex ( ) ;
m_pStringListModel-> removeRow ( index. row ( ) ) ;
}
void Widget :: on_btnInsert_clicked ( ) {
QModelIndex index= ui-> listView-> currentIndex ( ) ;
m_pStringListModel-> insertRow ( index. row ( ) ) ;
m_pStringListModel-> setData ( index, "inserted item" , Qt:: DisplayRole) ;
ui-> listView-> setCurrentIndex ( index) ;
}
void Widget :: on_btnReshow_clicked ( ) {
QStringList tmpList = m_pStringListModel-> stringList ( ) ;
ui-> textEdit-> clear ( ) ;
for ( int i = 0 ; i < tmpList. count ( ) ; i++ ) {
ui-> textEdit-> append ( tmpList. at ( i) ) ;
}
}
void Widget :: on_btnClearAllData_clicked ( ) {
m_pStringListModel-> removeRows ( 0 , m_pStringListModel-> rowCount ( ) ) ;
}
void Widget :: initMenu ( ) {
m_pMenu = new QMenu ( ui-> listView) ;
QAction * pAc1 = new QAction ( u8"删除" , ui-> listView) ;
QAction * pAc2 = new QAction ( u8"插入" , ui-> listView) ;
QAction * pAc3 = new QAction ( u8"置顶" , ui-> listView) ;
QAction * pAc4 = new QAction ( u8"排到最后" , ui-> listView) ;
m_pMenu-> addAction ( pAc1) ;
m_pMenu-> addAction ( pAc2) ;
m_pMenu-> addAction ( pAc3) ;
m_pMenu-> addAction ( pAc4) ;
connect ( pAc1, & QAction:: triggered, this , & Widget:: OnActionDelete) ;
}
void Widget :: on_showRightMenu ( const QPoint& pos) {
if ( ! ( ( ui-> listView-> selectionModel ( ) -> selectedIndexes ( ) ) . empty ( ) ) ) {
m_pMenu-> exec ( QCursor :: pos ( ) ) ;
}
}
void Widget :: OnActionDelete ( ) {
QModelIndex index = ui-> listView-> currentIndex ( ) ;
m_pStringListModel-> removeRow ( index. row ( ) ) ;
}
void Widget :: on_listView_clicked ( const QModelIndex & index) {
ui-> textEdit-> clear ( ) ;
ui-> textEdit-> append ( QString :: asprintf ( u8"当前项:row=%d, column=%d" ,
index. row ( ) , index. column ( ) ) ) ;
}