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_NAMESPACEclass Widget : public QWidget { Q_OBJECTpublic : 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_OBJECTpublic : 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_NAMESPACEclass Widget : public QWidget { Q_OBJECTpublic : 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 ( ) ) ) ;
}