├── .gitignore ├── CMakeLists.txt ├── README.md ├── images ├── 1.jpg └── 2.jpg └── src ├── CMakeLists.txt ├── HHeaderItemDelegate.cpp ├── HHeaderItemDelegate.h ├── HHeaderModel.cpp ├── HHeaderModel.h ├── HHeaderView.cpp ├── HHeaderView.h ├── MainWidget.cpp ├── MainWidget.h ├── TabelModel.cpp ├── TabelModel.h ├── TableView.cpp ├── TableView.h ├── VHeaderModel.cpp ├── VHeaderModel.h ├── VHeaderView.cpp ├── VHeaderView.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.user 4 | build/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.1.0) 2 | 3 | project(HeadView) 4 | 5 | 6 | set(CMAKE_VERBOSE_MAKEFILE ON) 7 | 8 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 9 | 10 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 11 | 12 | set(CMAKE_CXX_STANDARD 11) 13 | 14 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 15 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 16 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 17 | 18 | set(CMAKE_DEBUG_POSTFIX "d") 19 | 20 | add_subdirectory(src) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HeadView 2 | 3 | Qt model/view 实现复杂的多行表头,可用于自定义我自己的表头,Table的多行表头在treeView的头中又重新设置了一遍,核心是多行表头实际上是通过ItemDelegate手动绘制。 4 | 本例的基本功能是: 5 | - 自定义了一个表格View(TableView),支持继承重载相关虚函数 6 | - TabelView支持设置多行横向表头(默认2行) 7 | - 可以添加多张表格,每个表格是独立的,它们都有属于自己的自定义表头 8 | - 表头的右键操作 9 | 10 | ![多行表头](./images/1.jpg) 11 | ![表头菜单事件](./images/2.jpg) -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4OSL/HeadView/a19da8d74bc89aa855c89ad5c7b56daaab788e0f/images/1.jpg -------------------------------------------------------------------------------- /images/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/4OSL/HeadView/a19da8d74bc89aa855c89ad5c7b56daaab788e0f/images/2.jpg -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_AUTOMOC ON) 2 | set(CMAKE_AUTORCC ON) 3 | 4 | #QT库 5 | find_package(Qt5Widgets CONFIG REQUIRED) 6 | include_directories(${Qt5Widgets_INCLUDE_DIRS}) 7 | find_package(Qt5Core CONFIG REQUIRED) 8 | find_package(Qt5Gui CONFIG REQUIRED) 9 | find_package(Qt5Network CONFIG REQUIRED) 10 | include_directories(${Qt5Network_INCLUDE_DIRS}) 11 | find_package(Qt5PrintSupport CONFIG REQUIRED) 12 | include_directories(${Qt5PrintSupport_INCLUDE_DIRS}) 13 | find_package(Qt5Xml CONFIG REQUIRED) 14 | include_directories(${Qt5Xml_INCLUDE_DIRS}) 15 | find_package(Qt5Concurrent CONFIG REQUIRED) 16 | include_directories(${Qt5Concurrent_INCLUDE_DIRS}) 17 | find_package(Qt5AxContainer CONFIG REQUIRED) 18 | include_directories(${Qt5AxContainer_INCLUDE_DIRS}) 19 | 20 | 21 | file(GLOB SRC 22 | "${CMAKE_CURRENT_SOURCE_DIR}/*.txt" 23 | "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" 24 | "${CMAKE_CURRENT_SOURCE_DIR}/*.h") 25 | file(GLOB GUI_UI 26 | "${CMAKE_CURRENT_SOURCE_DIR}/*.ui") 27 | 28 | qt5_wrap_ui(Gui_gen_srcs ${GUI_UI}) 29 | add_executable(${CMAKE_PROJECT_NAME} ${SRC} ${Gui_gen_srcs}) 30 | 31 | target_link_libraries(${CMAKE_PROJECT_NAME} ${Qt5Core_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Widgets_LIBRARIES} 32 | ${Qt5Network_LIBRARIES} ${Qt5PrintSupport_LIBRARIES} ${Qt5Concurrent_LIBRARIES} Qt5::AxContainer) -------------------------------------------------------------------------------- /src/HHeaderItemDelegate.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "HHeaderItemDelegate.h" 5 | #include "HHeaderView.h" 6 | #include 7 | 8 | HHeaderItemDelegate::HHeaderItemDelegate(QObject *parent) : QStyledItemDelegate(parent) 9 | { 10 | } 11 | 12 | void HHeaderItemDelegate::setHeaderView(HHeaderView *pHeader) 13 | { 14 | m_pHeaderView = pHeader; 15 | } 16 | 17 | ///< headerView中绘制的item,根据不同的操作显示不同的效果 18 | void HHeaderItemDelegate::paint (QPainter * painter 19 | ,const QStyleOptionViewItem& option 20 | ,const QModelIndex &index) const 21 | { 22 | 23 | int row = index.row(); 24 | int col = index.column(); 25 | 26 | //const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; 27 | 28 | QRect smallRect; 29 | //判断是否有checkbox 30 | ///< 目前我是不会设置checkbox的 31 | if (m_pHeaderView->isCheckedEnable(row, col)) 32 | { 33 | smallRect = QStyle::alignedRect(option.direction, Qt::AlignCenter, 34 | QSize(option.fontMetrics.width(index.data(Qt::DisplayRole).toString()) + 25, option.fontMetrics.height() + 3), 35 | option.rect); 36 | } 37 | else 38 | { 39 | smallRect = QStyle::alignedRect(option.direction, Qt::AlignCenter, 40 | QSize(option.fontMetrics.width(index.data(Qt::DisplayRole).toString()) + 8, option.fontMetrics.height() + 3), 41 | option.rect); 42 | } 43 | 44 | QStyleOptionHeader header_opt; 45 | header_opt.rect = option.rect; 46 | header_opt.position = QStyleOptionHeader::Middle; 47 | header_opt.textAlignment = Qt::AlignCenter; 48 | 49 | header_opt.state = option.state; 50 | //header_opt.state |= QStyle::State_HasFocus;//QStyle::State_Enabled | QStyle::State_Horizontal | QStyle::State_None | QStyle::State_Raised; 51 | 52 | //判断是否能够排序 53 | ///< 支持排序 54 | if (m_pHeaderView->isSortedEnable(row, col)) 55 | { 56 | //可以排序 57 | //获取它当前的排序顺序 58 | int ret = m_pHeaderView->sortedDirection(row, col); 59 | if (0 == ret) 60 | { 61 | header_opt.sortIndicator = QStyleOptionHeader::SortUp; 62 | } 63 | else if (1 == ret) 64 | { 65 | header_opt.sortIndicator = QStyleOptionHeader::SortDown; 66 | } 67 | else 68 | { 69 | header_opt.sortIndicator = QStyleOptionHeader::None; 70 | } 71 | } 72 | if (m_pHeaderView->isItemPress(row, col)) 73 | { 74 | header_opt.state |= QStyle::State_Sunken; //按钮按下效果 75 | } 76 | 77 | painter->save(); 78 | QApplication::style()->drawControl(QStyle::CE_Header, &header_opt, painter); 79 | painter->restore(); 80 | painter->setPen(QColor(255,0,0)); 81 | QStyleOptionViewItemV4 xopt(option); 82 | xopt.state &= ~QStyle::State_MouseOver; 83 | if (m_pHeaderView->isCheckedEnable(row, col)) 84 | { 85 | //判断是该单元格是否有checkbox 86 | xopt.features |= QStyleOptionViewItemV4::HasCheckIndicator; 87 | 88 | //判断checkbox的状态 89 | Qt::CheckState state = m_pHeaderView->checkedState(row, col); 90 | if (Qt::Unchecked == state) 91 | { 92 | xopt.state |= QStyle::State_Off; 93 | } 94 | else if (Qt::PartiallyChecked == state) 95 | { 96 | xopt.state |= QStyle::State_NoChange; 97 | } 98 | else if (Qt::Checked == state) 99 | { 100 | xopt.state |= QStyle::State_On; 101 | } 102 | } 103 | 104 | xopt.rect = smallRect; 105 | if (!m_pHeaderView->isColHide(col)) 106 | { 107 | QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &xopt, painter); 108 | 109 | //首先要判断是否处于hover状态 110 | if (header_opt.state & QStyle::State_MouseOver) 111 | { 112 | //判断是否能够排序,如果能排序,则显示图标 113 | if (m_pHeaderView->isCustomMenuEnable(row, col)) 114 | { 115 | drawMenuBtn(row, col, option.rect, painter); 116 | } 117 | } 118 | 119 | //判断前面1列是否隐藏,如果隐藏了,需要在左边画上三角符号 120 | if (m_pHeaderView->isSectionHidden(col - 1)) 121 | { 122 | drawPreBtn(option.rect, painter); 123 | } 124 | 125 | //判断后面1列是否隐藏,如果隐藏了,需要在右边画上三角符号 126 | if (m_pHeaderView->isSectionHidden(col + 1)) 127 | { 128 | drawNextBtn(option.rect, painter); 129 | } 130 | } 131 | 132 | } 133 | 134 | 135 | bool HHeaderItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 136 | { 137 | return false; 138 | } 139 | 140 | void HHeaderItemDelegate::drawMenuBtn(int row, int col, const QRect& rect, QPainter* painter) const 141 | { 142 | QRect brect = QRect(rect.right() - 5 - 13, rect.center().y() - 6, 13, 13); 143 | 144 | //判断鼠标是否在menu的上面,如果是,则要用黑色显示,否则灰色 145 | int state = m_pHeaderView->customMenuState(row, col); 146 | bool enabled = (state == 1 ? true : false); 147 | 148 | painter->save(); 149 | painter->setPen(enabled ? QColor(186, 186, 186) : QColor(223, 223, 223)); 150 | painter->setBrush(QColor(246, 246, 246)); 151 | painter->drawRect(brect.adjusted(0, 0 ,-1, -1)); 152 | 153 | painter->setPen(enabled ? QColor(71, 71, 71) : QColor(193, 193, 193)); 154 | painter->drawLine(brect.left() + 3, brect.top() + 5, brect.right() - 3, brect.top() + 5); 155 | painter->drawLine(brect.left() + 4, brect.top() + 6, brect.right() - 4, brect.top() + 6); 156 | painter->drawLine(brect.left() + 5, brect.top() + 7, brect.right() - 5, brect.top() + 7); 157 | painter->drawPoint(brect.left() + 6, brect.top() + 8); 158 | painter->restore(); 159 | } 160 | 161 | void HHeaderItemDelegate::drawPreBtn(const QRect& rect, QPainter* painter) const 162 | { 163 | QRect brect = QRect(rect.left() + 1, rect.center().y() - 6, 13, 13); 164 | 165 | painter->save(); 166 | painter->setPen(QColor(71, 71, 71)); 167 | painter->drawLine(brect.left() + 1, brect.center().y() - 3, brect.left() + 1, brect.center().y() + 3); 168 | painter->drawLine(brect.left() + 2, brect.center().y() - 2, brect.left() + 2, brect.center().y() + 2); 169 | painter->drawLine(brect.left() + 3, brect.center().y() - 1, brect.left() + 3, brect.center().y() + 1); 170 | painter->drawPoint(brect.left() + 4, brect.center().y()); 171 | painter->restore(); 172 | } 173 | 174 | void HHeaderItemDelegate::drawNextBtn(const QRect& rect, QPainter* painter) const 175 | { 176 | QRect brect = QRect(rect.right() - 13, rect.center().y() - 6, 13, 13);; 177 | 178 | painter->save(); 179 | painter->setPen(QColor(71, 71, 71)); 180 | painter->drawLine(brect.right() - 2, brect.center().y() - 3, brect.right() - 2, brect.center().y() + 3); 181 | painter->drawLine(brect.right() - 3, brect.center().y() - 2, brect.right() - 3, brect.center().y() + 2); 182 | painter->drawLine(brect.right() - 4, brect.center().y() - 1, brect.right() - 4, brect.center().y() + 1); 183 | painter->drawPoint(brect.right() - 5, brect.center().y()); 184 | painter->restore(); 185 | } 186 | -------------------------------------------------------------------------------- /src/HHeaderItemDelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef HeaderItemDelegate_H 2 | #define HeaderItemDelegate_H 3 | 4 | #include 5 | 6 | class HHeaderView; 7 | class HHeaderItemDelegate : public QStyledItemDelegate 8 | { 9 | Q_OBJECT 10 | public: 11 | HHeaderItemDelegate(QObject *parent = 0); 12 | void setHeaderView(HHeaderView* pHeader); 13 | virtual void paint (QPainter *painter 14 | ,const QStyleOptionViewItem &option 15 | ,const QModelIndex &index )const; 16 | 17 | virtual bool editorEvent(QEvent *event 18 | ,QAbstractItemModel *model 19 | ,const QStyleOptionViewItem &option 20 | ,const QModelIndex &index); 21 | 22 | private: 23 | void drawMenuBtn(int row, int col, const QRect& rect, QPainter* painter) const; 24 | void drawPreBtn(const QRect& rect, QPainter* painter) const; 25 | void drawNextBtn(const QRect& rect, QPainter* painter) const; 26 | private: 27 | HHeaderView *m_pHeaderView; 28 | }; 29 | 30 | #endif // HeaderItemDelegate_H 31 | -------------------------------------------------------------------------------- /src/HHeaderModel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "HHeaderModel.h" 4 | 5 | HHeaderModel::HHeaderModel(QObject *parent) 6 | { 7 | m_nMaxCol = 0; 8 | } 9 | HHeaderModel::~HHeaderModel() 10 | { 11 | } 12 | /** 13 | ** 为model中各个单元格cell(row,col)设置待显示的值(就是一个文本) 14 | ** 无论该cell是否属于一个合并单元格,都要设置其值 15 | **/ 16 | void HHeaderModel::setItem(int row, int col, const QString& text) 17 | { 18 | ModelData* data = new ModelData(); 19 | data->strText = text; 20 | 21 | m_modelData[row][col] = data; 22 | 23 | if (m_nMaxCol < col) 24 | { 25 | m_nMaxCol = col; 26 | } 27 | } 28 | /** 29 | ** 获取cell(row,col)的文本内容 30 | **/ 31 | QString HHeaderModel::item(int row, int col) 32 | { 33 | QMap >::Iterator iterRow = m_modelData.find(row); 34 | if (iterRow == m_modelData.end() || iterRow.value().isEmpty()) 35 | { 36 | return QString(); 37 | } 38 | 39 | QMap::Iterator iterCol = iterRow.value().find(col); 40 | if (iterCol == iterRow.value().end()) 41 | { 42 | return QString(); 43 | } 44 | 45 | return iterCol.value()->strText; 46 | } 47 | /** 48 | ** param firstRow:本合并单元格的,起始行 49 | ** param firstColumn:本合并单元格的起始列 50 | ** param rowSpanCount:本合并单元格需要占用多少行 51 | ** param columnSpanCount:本合并单元格需要占用多少列 52 | */ 53 | void HHeaderModel::setSpan(int firstRow,int firstColumn 54 | ,int rowSpanCount,int columnSpanCount) 55 | { 56 | for(int row = firstRow; row < firstRow + rowSpanCount; ++row) 57 | { 58 | for(int col = firstColumn; col < firstColumn + columnSpanCount; ++col) 59 | { 60 | ///< 如果某一个合并单元格是从cell(0,0)开始,占用1行,4列 61 | ///< 那么cell(0,0),cell(0,1),cell(0,2),cell(0,3) 62 | ///< 就是属于这个合并单元的,都应当记录下来 63 | m_cellSpanList.append(CellSpan(row,col 64 | ,rowSpanCount,columnSpanCount 65 | ,firstRow,firstColumn)); 66 | } 67 | } 68 | } 69 | /** 70 | ** 获取cell(row,column)所在的合并单元格(CellSpan) 71 | **/ 72 | const CellSpan& HHeaderModel::getSpan(int row, int column) 73 | { 74 | for(QList::const_iterator iter = m_cellSpanList.begin(); iter != m_cellSpanList.end(); ++iter) 75 | { 76 | if((*iter).nCurRow == row && (*iter).nCurCol == column) 77 | { 78 | return *iter; 79 | } 80 | } 81 | 82 | return m_stInvalidCellSpan; 83 | } 84 | 85 | QModelIndex HHeaderModel::index(int row, int column, const QModelIndex &parent) const 86 | { 87 | Q_UNUSED(parent); 88 | if (row < 0 || column < 0 || m_modelData.isEmpty()) 89 | return QModelIndex(); 90 | 91 | MapHeaderData::const_iterator iterRow = m_modelData.find(row); 92 | if (iterRow == m_modelData.end()) 93 | return QModelIndex(); 94 | 95 | if (iterRow.value().isEmpty()) 96 | return QModelIndex(); 97 | 98 | QMap::const_iterator iterCol = iterRow.value().find(column); 99 | if (iterCol == iterRow.value().end()) 100 | return QModelIndex(); 101 | 102 | return createIndex(row, column, iterCol.value()); 103 | } 104 | 105 | QModelIndex HHeaderModel::parent(const QModelIndex &child) const 106 | { 107 | Q_UNUSED(child); 108 | return QModelIndex(); 109 | } 110 | 111 | int HHeaderModel::rowCount(const QModelIndex &parent) const 112 | { 113 | Q_UNUSED(parent); 114 | return m_modelData.size(); 115 | } 116 | 117 | int HHeaderModel::columnCount(const QModelIndex &parent) const 118 | { 119 | Q_UNUSED(parent); 120 | return m_nMaxCol + 1; 121 | } 122 | /** 123 | ** 设置值不考虑合并单元格,每一个index都要返回 124 | ** 从代码来看,似乎本函数并未被调用 125 | */ 126 | QVariant HHeaderModel::data(const QModelIndex &index, int role) const 127 | { 128 | int row = index.row(); 129 | int col = index.column(); 130 | 131 | if (Qt::DisplayRole == role || Qt::EditRole == role) 132 | { 133 | MapHeaderData::const_iterator iterRow = m_modelData.find(row); 134 | if (iterRow == m_modelData.end() || iterRow.value().isEmpty()) 135 | return QString(); 136 | 137 | QMap::const_iterator iterCol = iterRow.value().find(col); 138 | if (iterCol == iterRow.value().end()) 139 | { 140 | return QString(); 141 | } 142 | 143 | return iterCol.value()->strText; 144 | } 145 | return QVariant(); 146 | } 147 | 148 | Qt::ItemFlags HHeaderModel::flags(const QModelIndex &index) const 149 | { 150 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; 151 | } 152 | 153 | bool HHeaderModel::setData ( const QModelIndex & index, const QVariant & value, int role) 154 | { 155 | return false; 156 | } 157 | 158 | 159 | HHeaderModel::ModelData* HHeaderModel::modelData(const QModelIndex & index) const 160 | { 161 | if (!index.isValid()) 162 | return NULL; 163 | 164 | int row = index.row(); 165 | int col = index.column(); 166 | 167 | MapHeaderData::const_iterator iterRow = m_modelData.find(row); 168 | if (iterRow == m_modelData.end() || iterRow.value().isEmpty()) 169 | return NULL; 170 | 171 | QMap::const_iterator iterCol = iterRow.value().find(col); 172 | if (iterCol == iterRow.value().end()) 173 | { 174 | return NULL; 175 | } 176 | 177 | return iterCol.value(); 178 | } 179 | 180 | QVariant HHeaderModel::headerData(int section, Qt::Orientation orientation, int role) const 181 | { 182 | //重载了sectionSizeFromContents函数,但没有调用默认的处理,所以不会走到这里。 183 | if (role == Qt::SizeHintRole) 184 | { 185 | return QSize(200, 100); 186 | } 187 | 188 | return QVariant(); 189 | 190 | // return QAbstractItemModel::headerData(section,orientation,role); 191 | } 192 | -------------------------------------------------------------------------------- /src/HHeaderModel.h: -------------------------------------------------------------------------------- 1 | #ifndef HHEADERMODEL_H 2 | #define HHEADERMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | /** 10 | * @brief 单元格合并大小 11 | */ 12 | struct CellSpan 13 | { 14 | CellSpan() : nCurRow(-1),nCurCol(-1) 15 | , nRowSpan(-1), nColSpan(-1) 16 | , nFirstRow(-1), nFirstCol(-1) 17 | , bValidFlag(false) 18 | { 19 | } 20 | 21 | CellSpan(int row, int col, int spanRow 22 | ,int spanCol, int firstRow, int firstCol) 23 | { 24 | nCurRow = row; 25 | nCurCol = col; 26 | nRowSpan = spanRow; 27 | nColSpan = spanCol; 28 | nFirstRow = firstRow; 29 | nFirstCol = firstCol; 30 | bValidFlag = true; 31 | } 32 | 33 | bool isValid() 34 | { 35 | return bValidFlag; 36 | } 37 | 38 | void print() 39 | { 40 | qDebug()<<"nCurRow :"<> MapHeaderData; 71 | 72 | public: 73 | 74 | HHeaderModel(QObject *parent = 0); 75 | ~HHeaderModel(); 76 | 77 | void setItem(int row, int col, const QString& text); 78 | 79 | QString item(int row, int col); 80 | 81 | void setSpan(int firstRow, int firstColumn, int rowSpanCount, int columnSpanCount); 82 | const CellSpan& getSpan(int row, int column); 83 | 84 | virtual QModelIndex index(int row, int column, const QModelIndex &parent) const; 85 | virtual QModelIndex parent(const QModelIndex &child) const; 86 | virtual int rowCount(const QModelIndex &parent) const; 87 | virtual int columnCount(const QModelIndex &parent) const; 88 | virtual QVariant data(const QModelIndex &index, int role) const; 89 | virtual Qt::ItemFlags flags(const QModelIndex &index) const; 90 | 91 | virtual bool setData ( const QModelIndex & index, const QVariant & value, int role = Qt::EditRole ); 92 | 93 | virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; 94 | private: 95 | //找到对应的模型数据 96 | ModelData* modelData(const QModelIndex & index) const; 97 | 98 | private: 99 | 100 | MapHeaderData m_modelData; 101 | int m_nMaxCol; 102 | 103 | CellSpan m_stInvalidCellSpan; 104 | QList m_cellSpanList; 105 | 106 | }; 107 | 108 | 109 | #endif // HHEADERMODEL_H 110 | -------------------------------------------------------------------------------- /src/HHeaderView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "HHeaderView.h" 10 | #include "HHeaderItemDelegate.h" 11 | #include "HHeaderModel.h" 12 | //#include "DataTreeView.h" 13 | 14 | HHeaderView::HHeaderView(Qt::Orientation orientation, QWidget * parent) : QHeaderView(orientation, parent),m_bQuit(false) 15 | { 16 | ///< 设置代理,由代理进行画单元格 17 | m_pItemDelegate = new HHeaderItemDelegate(); 18 | m_pItemDelegate->setHeaderView(this); 19 | setItemDelegate(m_pItemDelegate); 20 | 21 | connect(this, SIGNAL(sectionResized(int,int,int)), this, SLOT(onSectionResized(int,int,int))); 22 | ///< 交互式,代表支持鼠标拖动 23 | setSectionResizeMode(QHeaderView::Interactive); 24 | //setSectionResizeMode(QHeaderView::Stretch); 25 | setCascadingSectionResizes(true); 26 | 27 | this->setOffset(0); ///< 这将会影响item绘制的位置 28 | setSectionsMovable(true); 29 | setSectionsClickable(true); 30 | setMinimumSectionSize(80); 31 | setStretchLastSection(true); 32 | setHighlightSections(false); 33 | setMouseTracking(false); 34 | 35 | setAttribute(Qt::WA_Hover, false); 36 | setDefaultSectionSize(100); 37 | 38 | m_menu = new QMenu(this); 39 | m_actHideCol = new QAction(this); 40 | m_actHideCol->setText("hide column"); 41 | m_actsortUp = new QAction(this); 42 | m_actsortUp->setText("sort A->Z"); 43 | m_actSortDown = new QAction(this); 44 | m_actSortDown->setText("sort Z->A"); 45 | 46 | m_menu->addAction(m_actHideCol); 47 | m_menu->addAction(m_actsortUp); 48 | m_menu->addAction(m_actSortDown); 49 | 50 | m_menu->hide(); 51 | } 52 | 53 | HHeaderView::~HHeaderView() 54 | { 55 | if(m_pItemDelegate) 56 | { 57 | delete m_pItemDelegate; 58 | m_pItemDelegate = NULL; 59 | } 60 | } 61 | 62 | void HHeaderView::initHeaderView(HHeaderModel *pModel) 63 | { 64 | ///< 设置各个独立单元格Item 65 | pModel->setItem(0,0, QStringLiteral("张三")); 66 | pModel->setItem(0,2, QStringLiteral("李四")); 67 | pModel->setItem(0,4, QStringLiteral("王五")); 68 | 69 | pModel->setItem(1,0, QStringLiteral("语文")); 70 | pModel->setItem(1,1, QStringLiteral("数学")); 71 | pModel->setItem(1,2, QStringLiteral("语文")); 72 | pModel->setItem(1,3, QStringLiteral("数学")); 73 | pModel->setItem(1,4, QStringLiteral("语文")); 74 | pModel->setItem(1,5, QStringLiteral("数学")); 75 | 76 | ///< 设置合并单元格 77 | pModel->setSpan(0,0,1,2); 78 | pModel->setSpan(0,2,1,2); 79 | pModel->setSpan(0,4,1,2); 80 | 81 | } 82 | 83 | void HHeaderView::setQuit() 84 | { 85 | m_bQuit = true; 86 | } 87 | 88 | int HHeaderView::sectionSizes(int lIndex, int sectionCount) 89 | { 90 | int width = 0; 91 | for(int i = lIndex; i < lIndex + sectionCount; ++i) 92 | { 93 | width += sectionSize(i); 94 | } 95 | 96 | return width; 97 | } 98 | 99 | QSize HHeaderView::sectionSizeFromContents(int logicalIndex) const 100 | { 101 | //return QHeaderView::sectionSizeFromContents(logicalIndex); 102 | QSize size; 103 | int maxWidth = 0; 104 | 105 | HHeaderModel* model = static_cast (this->model()); 106 | for(int i = 0; i < model->rowCount(QModelIndex()); ++i) 107 | { 108 | QFont fnt; 109 | QStyleOptionHeader opt; 110 | initStyleOption(&opt); 111 | fnt.setBold(true); 112 | opt.text = model->item(i, logicalIndex); 113 | opt.fontMetrics = QFontMetrics(fnt); 114 | size = style()->sizeFromContents(QStyle::CT_HeaderSection, &opt, QSize(), this); 115 | maxWidth = qMax(maxWidth, size.width()); 116 | } 117 | ///< 主要是设置高度,宽度这里设指了没有用, 118 | ///< 需要由headerview的父窗体里设置 119 | return QSize(maxWidth+4,size.height()*model->rowCount(QModelIndex())); 120 | } 121 | /** 122 | ** 这里实质上没有绘制任何item,而是调用delegate的paint函数 123 | ** 进行绘制item的 124 | */ 125 | void HHeaderView::paintEvent(QPaintEvent *event) 126 | { 127 | Q_UNUSED(event) 128 | QPainter painter(viewport()); 129 | QMultiMap rowSpanList; 130 | 131 | int cnt = this->count (); 132 | int curRow, curCol; 133 | HHeaderModel* model = static_cast (this->model()); 134 | for(int row = 0; row < model->rowCount(QModelIndex()); ++row) 135 | { 136 | for(int col = 0; col < model->columnCount(QModelIndex()); ++col) 137 | { 138 | curRow = row; 139 | curCol = col; 140 | 141 | ///< 设置style 142 | QStyleOptionViewItemV4 opt = viewOptions(); 143 | QStyleOptionHeader header_opt; 144 | initStyleOption(&header_opt); 145 | 146 | header_opt.textAlignment = Qt::AlignCenter; 147 | //header_opt.icon = QIcon("./Resources/logo.ico"); 148 | QFont fnt; 149 | fnt.setBold(true); 150 | header_opt.fontMetrics = QFontMetrics(fnt); 151 | opt.fontMetrics = QFontMetrics(fnt); 152 | QSize size = style()->sizeFromContents(QStyle::CT_HeaderSection, &header_opt, QSize(), this); 153 | //size.setHeight(25); 154 | header_opt.position = QStyleOptionHeader::Middle; 155 | 156 | ///< 判断当前行是否处于鼠标悬停状态 157 | if(m_hoverIndex == model->index(row, col, QModelIndex())) 158 | { 159 | header_opt.state |= QStyle::State_MouseOver; 160 | // header_opt.state |= QStyle::State_Active; 161 | } 162 | 163 | ///< 获取某个cell要显示的文本 164 | opt.text = model->item(row, col); 165 | header_opt.text = model->item(row, col); 166 | 167 | ///< 获取某个cell所在的合并单元格 168 | CellSpan span = model->getSpan(row, col); 169 | 170 | ///< 本单元格横跨几行,几列? 171 | int rowSpan = span.nRowSpan; 172 | int columnSpan = span.nColSpan; 173 | 174 | if(columnSpan > 1 && rowSpan > 1) 175 | { 176 | ///< 单元格跨越多列和多行, 不支持,改为多行1列 177 | continue; 178 | /*header_opt.rect = QRect(sectionViewportPosition(logicalIndex(col)), row * size.height(), sectionSizes(col, columnSpan), rowSpan * size.height()); 179 | opt.rect = header_opt.rect; 180 | col += columnSpan - 1; */ 181 | } 182 | else if(columnSpan > 1) 183 | { 184 | ///< 单元格跨越多列 185 | header_opt.rect = QRect(sectionViewportPosition(logicalIndex(col)) 186 | ,row*size.height() 187 | ,sectionSizes(col,columnSpan) 188 | ,size.height()); 189 | 190 | opt.rect = header_opt.rect; 191 | col += columnSpan - 1; 192 | } 193 | else if(rowSpan > 1) 194 | { 195 | ///< 单元格跨越多行 196 | header_opt.rect = QRect(sectionViewportPosition(logicalIndex(col)) 197 | ,row*size.height() 198 | ,sectionSize(logicalIndex(col)) 199 | ,size.height()*rowSpan); 200 | 201 | opt.rect = header_opt.rect; 202 | ///< rect 遍历到合并单元格中的左上角时,整个合并单元格已经形成 203 | ///< 合并单元格的其他小格子,就不需要绘制了 204 | ///< rowSpanList中记录的就是不需要绘制的小格子了 205 | for(int i = row + 1; i <= rowSpan - 1; ++i) 206 | { 207 | rowSpanList.insert(i, col); 208 | } 209 | } 210 | else 211 | { 212 | ///< 正常的单元格 213 | header_opt.rect = QRect(sectionViewportPosition(logicalIndex(col)) 214 | ,row * size.height() 215 | ,sectionSize(logicalIndex(col)) 216 | ,size.height()); 217 | 218 | opt.rect = header_opt.rect; 219 | } 220 | 221 | opt.state = header_opt.state; 222 | //opt.displayAlignment = Qt::AlignCenter; 223 | opt.icon = QIcon("./Resources/logo.ico"); 224 | //opt.backgroundBrush = QBrush( QColor(255,0,0)); 225 | QMultiMap::iterator it = rowSpanList.find(curRow, curCol); 226 | if(it == rowSpanList.end()) 227 | { 228 | ///< 保存当前item的矩形 229 | m_itemRectMap[curRow][curCol] = header_opt.rect; 230 | m_pItemDelegate->paint(&painter 231 | ,opt 232 | ,model->index(curRow, curCol, QModelIndex())); 233 | } 234 | } 235 | } 236 | } 237 | 238 | void HHeaderView::onSectionResized(int logicalIndex, int oldSize, int newSize) 239 | { 240 | if (0 == newSize) 241 | { 242 | //过滤掉隐藏列导致的resize 243 | viewport()->update(); 244 | return; 245 | } 246 | 247 | static bool selfEmitFlag = false; 248 | if (selfEmitFlag) 249 | { 250 | return; 251 | } 252 | 253 | int minWidth = 99999; 254 | QFontMetrics metrics(this->font()); 255 | //获取这列上最小的字体宽度,移动的长度不能大于最小的字体宽度 256 | HHeaderModel* model = static_cast (this->model()); 257 | for(int i = 0; i < model->rowCount(QModelIndex()); ++i) 258 | { 259 | QString text = model->item(i, logicalIndex); 260 | if (text.isEmpty()) 261 | continue; 262 | 263 | int textWidth = metrics.width(text); 264 | if (minWidth > textWidth) 265 | { 266 | minWidth = textWidth; 267 | } 268 | } 269 | 270 | if (newSize < minWidth) 271 | { 272 | selfEmitFlag = true; 273 | this->resizeSection(logicalIndex, oldSize); 274 | selfEmitFlag = false; 275 | } 276 | 277 | viewport()->update(); 278 | } 279 | /* 280 | int HHeaderView::sectionSizeHint(int logicalIndex) 281 | { 282 | if (logicalIndex == 0) 283 | { 284 | return 10; 285 | } 286 | } 287 | */ 288 | //算出当前鼠标位置所在的index 289 | QModelIndex HHeaderView::indexAt(const QPoint& pos) const 290 | { 291 | int x = pos.x(); 292 | int y = pos.y(); 293 | 294 | QMap::const_iterator iterCol; 295 | QMap >::const_iterator iterRow = m_itemRectMap.begin(); 296 | for (; iterRow != m_itemRectMap.end(); ++iterRow) 297 | { 298 | for (iterCol = iterRow.value().begin(); iterCol != iterRow.value().end(); ++iterCol) 299 | { 300 | if(x > iterCol.value().x() && x < iterCol.value().x() + iterCol.value().width() && 301 | y > iterCol.value().y() && y < iterCol.value().y() + iterCol.value().height()) 302 | { 303 | return model()->index(iterRow.key(), iterCol.key(), QModelIndex()); 304 | } 305 | } 306 | } 307 | 308 | return QModelIndex(); 309 | } 310 | 311 | void HHeaderView::mouseMoveEvent(QMouseEvent *event) 312 | { 313 | QHeaderView::mouseMoveEvent(event); 314 | QModelIndex index = indexAt(event->pos()); 315 | if (!index.isValid()) 316 | { 317 | QHeaderView::mouseMoveEvent(event); 318 | return; 319 | } 320 | 321 | //保存当前hover的index 322 | m_hoverIndex = index; 323 | 324 | int row = index.row(); 325 | int col = index.column(); 326 | //判断是否有自定义menu 327 | if (isCustomMenuEnable(row, col)) 328 | { 329 | QRect rect = menuRect(row, col); 330 | QPoint pos = this->mapFromGlobal(QCursor::pos()); 331 | if (rect.contains(pos)) 332 | { 333 | setCustomMenuState(row, col, 1); 334 | } 335 | else 336 | { 337 | setCustomMenuState(row, col, 0); 338 | } 339 | } 340 | 341 | viewport()->update(); 342 | QHeaderView::mouseMoveEvent(event); 343 | } 344 | 345 | void HHeaderView::mousePressEvent ( QMouseEvent * event ) 346 | { 347 | m_pressIndex = indexAt(event->pos()); 348 | viewport()->update(); 349 | QHeaderView::mousePressEvent(event); 350 | } 351 | 352 | void HHeaderView::mouseReleaseEvent(QMouseEvent *event) 353 | { 354 | m_pressIndex = QModelIndex(); 355 | 356 | int row = indexAt(event->pos()).row(); 357 | int col = indexAt(event->pos()).column(); 358 | //判断是否可以check 359 | if (isCheckedEnable(row, col)) 360 | { 361 | Qt::CheckState state = checkedState(row, col) == Qt::Unchecked ? Qt::Checked : Qt::Unchecked; 362 | setCheckedState(row, col, state); 363 | 364 | //需要同步更新dataview的选中状态 365 | //DataTreeView::_Instance->updateCheckStateByCol(col, state); 366 | } 367 | //判断是否可以排序 368 | if (isSortedEnable(row, col)) 369 | { 370 | int direction = sortedDirection(row, col) != 1 ? 1 : 0; 371 | setSortedDirection(row, col, direction); 372 | 373 | if (1 == direction) 374 | { 375 | sortDown(row, col); 376 | } 377 | else if (0 == direction) 378 | { 379 | sortUp(row, col); 380 | } 381 | } 382 | 383 | QPoint pos = this->mapFromGlobal(QCursor::pos()); 384 | 385 | //判断是否有自定义menu 386 | if (isCustomMenuEnable(row, col)) 387 | { 388 | QRect rect = menuRect(row, col); 389 | if (rect.contains(pos)) 390 | { 391 | m_actHideCol->setEnabled(hiddenSectionCount() < count() - 1); 392 | QAction* act = m_menu->exec(mapToGlobal(event->pos())); 393 | if (act == m_actHideCol) 394 | { 395 | m_hideColSet.insert(col); // 保存到隐藏列表中 396 | 397 | hideSection(col); 398 | //updateSection(col - 1); 399 | } 400 | else if (act == m_actsortUp) 401 | { 402 | sortUp(row, col); 403 | } 404 | else if (act == m_actSortDown) 405 | { 406 | sortDown(row, col); 407 | } 408 | } 409 | } 410 | 411 | //判断前面的列是否隐藏 412 | if (isColHide(col - 1)) 413 | { 414 | QRect rect = prevRect(row, col); 415 | if (rect.contains(pos)) 416 | { 417 | showSection(col - 1); 418 | //updateSection(col - 2); 419 | 420 | //从隐藏列表中删除记录 421 | m_hideColSet.remove(col - 1); 422 | } 423 | } 424 | 425 | //判断后面的列是否隐藏 426 | if (isColHide(col + 1)) 427 | { 428 | QRect rect = nextRect(row, col); 429 | if (rect.contains(pos)) 430 | { 431 | showSection(col + 1); 432 | //updateSection(col + 2); 433 | 434 | //从隐藏列表中删除记录 435 | m_hideColSet.remove(col + 1); 436 | } 437 | } 438 | 439 | //需要更新界面 440 | viewport()->update(); 441 | 442 | QHeaderView::mouseReleaseEvent(event); 443 | } 444 | 445 | bool HHeaderView::event(QEvent* event ) 446 | { 447 | if (event->type() == QEvent::Leave) 448 | {//处理离开headerview时,取消hover状态 449 | m_hoverIndex = QModelIndex(); 450 | viewport()->update(); //这里需要update,要不界面不更新 451 | } 452 | 453 | return true; 454 | } 455 | 456 | 457 | void HHeaderView::setCheckedEnable(int row, int col, bool Flag) 458 | { 459 | m_itemStateMap[row][col].bIsChecked = Flag; 460 | } 461 | 462 | bool HHeaderView::isCheckedEnable(int row, int col) 463 | { 464 | // if(m_itemStateMap.isEmpty()) 465 | // { 466 | // return false; 467 | // } 468 | // return m_itemStateMap[row][col].bIsChecked; 469 | return false; 470 | } 471 | 472 | void HHeaderView::setCheckedState(int row, int col, Qt::CheckState state) 473 | { 474 | m_itemStateMap[row][col].enCheckedState = state; 475 | } 476 | 477 | Qt::CheckState HHeaderView::checkedState(int row, int col) 478 | { 479 | return m_itemStateMap[row][col].enCheckedState; 480 | } 481 | 482 | void HHeaderView::setSortedEnable(int row, int col, bool Flag) 483 | { 484 | m_itemStateMap[row][col].bIsSorted = Flag; 485 | } 486 | 487 | bool HHeaderView::isSortedEnable(int row, int col) 488 | { 489 | return m_itemStateMap[row][col].bIsSorted; 490 | } 491 | 492 | void HHeaderView::setSortedDirection(int row, int col, int direction) 493 | { 494 | m_itemStateMap[row][col].nSortedDirection = direction; 495 | } 496 | 497 | int HHeaderView::sortedDirection(int row, int col) 498 | { 499 | return m_itemStateMap[row][col].nSortedDirection; 500 | } 501 | 502 | void HHeaderView::setCustomMenuEnbale(int row, int col, bool Flag) 503 | { 504 | m_itemStateMap[row][col].bIsCustomMenu = Flag; 505 | } 506 | 507 | bool HHeaderView::isCustomMenuEnable(int row, int col) 508 | { 509 | return m_itemStateMap[row][col].bIsCustomMenu; 510 | } 511 | 512 | void HHeaderView::setCustomMenuState(int row, int col, int state) 513 | { 514 | m_itemStateMap[row][col].nMenuEnabled = state; 515 | } 516 | 517 | int HHeaderView::customMenuState(int row, int col) 518 | { 519 | return m_itemStateMap[row][col].nMenuEnabled; 520 | } 521 | 522 | QRect HHeaderView::menuRect(int row, int col) 523 | { 524 | QRect sectionRect = m_itemRectMap[row][col]; 525 | return QRect(sectionRect.right() - 5 - 13, sectionRect.center().y() - 6, 13, 13); 526 | } 527 | 528 | bool HHeaderView::isColHide(int col) 529 | { 530 | if (m_hideColSet.end() == m_hideColSet.find(col)) 531 | return false; 532 | 533 | return true; 534 | } 535 | 536 | QRect HHeaderView::prevRect(int row, int col) 537 | { 538 | QRect sectionRect = m_itemRectMap[row][col]; 539 | return QRect(sectionRect.left() + 1, sectionRect.center().y() - 6, 13, 13); 540 | } 541 | 542 | QRect HHeaderView::nextRect(int row, int col) 543 | { 544 | QRect sectionRect = m_itemRectMap[row][col]; 545 | return QRect(sectionRect.right() - 13, sectionRect.center().y() - 6, 13, 13); 546 | } 547 | 548 | void HHeaderView::sortUp(int row, int col) 549 | { 550 | qDebug()<<"[test]"<<"sort up"<count(); 574 | int width = this->sectionSize(count - 1); 575 | 576 | if (increase) 577 | { 578 | this->resizeSection(count - 1, width + 1); 579 | } 580 | else 581 | { 582 | this->resizeSection(count - 1, width - 1); 583 | } 584 | } 585 | -------------------------------------------------------------------------------- /src/HHeaderView.h: -------------------------------------------------------------------------------- 1 | #ifndef HHEADERVIEW_H 2 | #define HHEADERVIEW_H 3 | 4 | #include 5 | 6 | class HHeaderModel; 7 | 8 | struct ItemState 9 | { 10 | bool bIsChecked; 11 | ///< 0:unchecked 1:PartiallyChecked 2:Checked 12 | ///< 标示Item选择状态 13 | Qt::CheckState enCheckedState; 14 | 15 | bool bIsSorted; 16 | ///< 0:up 1:down 2:unSorted 17 | int nSortedDirection; 18 | 19 | bool bIsCustomMenu; 20 | ///< 0: unEnable 1: enable 21 | int nMenuEnabled; 22 | 23 | ItemState() 24 | { 25 | bIsChecked = false; 26 | enCheckedState = Qt::Unchecked; 27 | bIsSorted = false; 28 | nSortedDirection = 2; 29 | bIsCustomMenu = false; 30 | nMenuEnabled = 0; 31 | } 32 | }; 33 | 34 | class HHeaderItemDelegate; 35 | class HHeaderView : public QHeaderView 36 | { 37 | Q_OBJECT 38 | public: 39 | HHeaderView(Qt::Orientation orientation, QWidget * parent = 0); 40 | ~HHeaderView(); 41 | 42 | void initHeaderView(HHeaderModel *pModel); 43 | void setQuit(); 44 | 45 | int sectionSizes(int lIndex, int sectionCount); 46 | 47 | ///< 算出当前鼠标位置所在的index 48 | QModelIndex indexAt(const QPoint &pos) const; 49 | 50 | ///< 设置支持checked 51 | void setCheckedEnable(int row, int col, bool Flag = true); 52 | bool isCheckedEnable(int row, int col); 53 | void setCheckedState(int row, int col, Qt::CheckState state); 54 | Qt::CheckState checkedState(int row, int col); 55 | 56 | ///< 设置是否支持排序 57 | void setSortedEnable(int row, int col, bool Flag = true); 58 | bool isSortedEnable(int row, int col); 59 | void setSortedDirection(int row, int col, int direction); 60 | int sortedDirection(int row, int col); 61 | 62 | ///< 设置是否支持自定义menu 63 | void setCustomMenuEnbale(int row, int col, bool Flag = true); 64 | bool isCustomMenuEnable(int row, int col); 65 | void setCustomMenuState(int row, int col, int state); 66 | int customMenuState(int row, int col); 67 | 68 | ///< 判断该列是否隐藏 69 | bool isColHide(int col); 70 | 71 | ///< 判断该单元格是否按住 72 | bool isItemPress(int row, int col); 73 | 74 | ///< 重设最后一列的宽度,在tree收缩时加1,伸展时减1。实质上是想通过列的resize函数引起整个水平头的位置更新。 75 | ///< 不通过这样的处理,在tree的伸展收缩或者整个界面隐藏和展示时会水平头的位置不正确。 76 | void resizeLastSection(bool increase); 77 | public slots: 78 | void onSectionResized(int logicalIndex, int oldSize, int newSize); 79 | 80 | protected: 81 | virtual QSize sectionSizeFromContents(int logicalIndex) const; 82 | virtual void paintEvent(QPaintEvent *event); 83 | //virtual int sectionSizeHint(int logicalIndex); 84 | 85 | virtual void mouseMoveEvent(QMouseEvent *event); 86 | virtual bool event ( QEvent * event ); 87 | virtual void mousePressEvent ( QMouseEvent * event ); 88 | virtual void mouseReleaseEvent(QMouseEvent *event); 89 | 90 | private: 91 | ///< 自定义菜单rect 92 | QRect menuRect(int row, int col); 93 | ///< 隐藏前一列的icon的rect 94 | QRect prevRect(int row, int col); 95 | ///< 隐藏后一列的icon的rect 96 | QRect nextRect(int row, int col); 97 | 98 | void sortUp(int row, int col); 99 | void sortDown(int row, int col); 100 | private: 101 | QModelIndex m_hoverIndex; 102 | ///< 保存按下的列 103 | QModelIndex m_pressIndex; 104 | ///< 保存所有item的矩形 key:row key1:col 105 | QMap > m_itemRectMap; 106 | ///< 保存每个item能支持的style 107 | QMap > m_itemStateMap; 108 | ///< 保存隐藏列的记录 109 | QSet m_hideColSet; 110 | 111 | private: 112 | QMenu* m_menu; 113 | QAction* m_actHideCol; ///< 隐藏列 114 | QAction* m_actsortUp; ///< 升序 115 | QAction* m_actSortDown; ///< 降序 116 | 117 | bool m_bQuit; ///< 不允许继续绘制item 118 | HHeaderItemDelegate* m_pItemDelegate; 119 | }; 120 | 121 | #endif // HHEADERVIEW_H 122 | -------------------------------------------------------------------------------- /src/MainWidget.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "TableView.h" 8 | #include "TabelModel.h" 9 | 10 | MainWidget::MainWidget(QWidget *parent) : 11 | QWidget(parent) 12 | { 13 | // 表格数据 14 | QList > data; 15 | for(int i = 0; i < 10000; ++i) 16 | { 17 | QList list; 18 | for(int j = 0; j < 6; ++j) 19 | { 20 | list.append(i*j); 21 | } 22 | data.append(list); 23 | } 24 | // 表头数据 25 | QStringList headList; 26 | headList << QStringLiteral(" ") <setHeaderList(headList); 33 | pTabelModel->updateData(data); 34 | 35 | // 为表格内部(不是表头)设置数据的model 36 | pTableView->setModel(pTabelModel); 37 | pTableView->initHeaderView(); 38 | pTableView->initAction(); 39 | 40 | QHBoxLayout *pLayout = new QHBoxLayout(); 41 | pLayout->setSpacing(0); 42 | pLayout->addWidget(pTableView); 43 | this->setLayout(pLayout); 44 | 45 | 46 | } 47 | 48 | MainWidget::~MainWidget() 49 | { 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/MainWidget.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWIDGET_H 2 | #define MAINWIDGET_H 3 | 4 | #include 5 | 6 | 7 | class MainWidget : public QWidget 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | explicit MainWidget(QWidget *parent = 0); 13 | ~MainWidget(); 14 | 15 | private: 16 | 17 | }; 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /src/TabelModel.cpp: -------------------------------------------------------------------------------- 1 | #include "TabelModel.h" 2 | #include 3 | 4 | TabelModel::TabelModel(QObject *parent) : QAbstractTableModel(parent) 5 | { 6 | 7 | } 8 | 9 | TabelModel::~TabelModel() 10 | { 11 | m_headerList.clear(); 12 | m_data.clear(); 13 | } 14 | 15 | int TabelModel::rowCount(const QModelIndex &parent) const 16 | { 17 | return m_data.count(); 18 | } 19 | 20 | int TabelModel::columnCount(const QModelIndex &) const 21 | { 22 | return m_headerList.count(); 23 | } 24 | 25 | ///< 设置表格单元格数据内容 26 | QVariant TabelModel::data(const QModelIndex &index, int role) const 27 | { 28 | if (!index.isValid()) 29 | { 30 | return QVariant(); 31 | } 32 | 33 | if (role == Qt::DisplayRole) 34 | { 35 | const int row = index.row(); 36 | const int column = index.column(); 37 | if (row >= m_data.size()) 38 | return QVariant(); 39 | const int colSize = m_data.at(row).size(); 40 | if (column >= colSize) 41 | return QVariant(); 42 | return m_data.at(row).at(column); 43 | } 44 | else 45 | { 46 | return QVariant(); 47 | } 48 | } 49 | 50 | ///< 因为表头已经有自定义的了,本函数不会被调用 51 | QVariant TabelModel::headerData(int section, Qt::Orientation orientation, int role) const 52 | { 53 | if (role != Qt::DisplayRole) 54 | return QVariant(); 55 | if (orientation == Qt::Horizontal) 56 | { 57 | return QVariant(m_headerList.at(section)); 58 | } 59 | else if(orientation == Qt::Vertical) 60 | { 61 | return QVariant(section+1); 62 | } 63 | else 64 | { 65 | return QVariant(); 66 | } 67 | } 68 | 69 | void TabelModel::setHeaderList(const QStringList& list) 70 | { 71 | m_headerList = list; 72 | } 73 | 74 | void TabelModel::updateData(const QList >&listVariant) 75 | { 76 | m_data = listVariant; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/TabelModel.h: -------------------------------------------------------------------------------- 1 | #ifndef TABELMODEL_H 2 | #define TABELMODEL_H 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * @brief 自定义列表模型 9 | */ 10 | class TabelModel : public QAbstractTableModel 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit TabelModel(QObject *parent = 0); 15 | ~TabelModel(); 16 | int rowCount(const QModelIndex &parent = QModelIndex()) const; 17 | int columnCount(const QModelIndex &) const; 18 | 19 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 20 | QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 21 | 22 | void setHeaderList(const QStringList&); 23 | void updateData(const QList >&); 24 | private: 25 | QStringList m_headerList; ////< 表头标题 26 | QList > m_data; ////< 表格内容(QVariant万能数据类型保证表格数据的多元性) 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /src/TableView.cpp: -------------------------------------------------------------------------------- 1 | #include "TableView.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "HHeaderView.h" 7 | #include "HHeaderModel.h" 8 | 9 | 10 | TableView::TableView(QWidget *parent) : QTableView(parent) 11 | { 12 | } 13 | 14 | TableView::~TableView() 15 | { 16 | QAbstractItemModel *pModel = model(); 17 | delete pModel; 18 | pModel = NULL; 19 | } 20 | 21 | void TableView::initHeaderView() 22 | { 23 | HHeaderView *pHeadView = new HHeaderView(Qt::Horizontal,this); 24 | HHeaderModel *pHeadModel = new HHeaderModel(); 25 | 26 | pHeadView->initHeaderView(pHeadModel); 27 | pHeadView->setModel(pHeadModel); 28 | setHorizontalHeader(pHeadView); 29 | } 30 | 31 | void TableView::initAction() 32 | { 33 | horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); 34 | verticalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); 35 | 36 | m_popMenu = new QMenu(this); 37 | m_popMenu->setStyleSheet("QMenu{background-color:rgb(255,255,255);color:rgb(0, 0, 0);font:10pt ""宋体"";}" 38 | "QMenu::item:selected{background-color:#CCDAE7;}"); 39 | 40 | m_actionDel = new QAction(QStringLiteral("删除"),this); 41 | m_actionMod = new QAction(QStringLiteral("修改"),this); 42 | m_actionCreate = new QAction(QStringLiteral("新建"),this); 43 | 44 | 45 | connect(m_actionDel,SIGNAL(triggered()),this,SLOT(onDeleteVariable())); 46 | connect(m_actionMod,SIGNAL(triggered()),this,SLOT(onModifyVariable())); 47 | connect(m_actionCreate,SIGNAL(triggered()),this,SLOT(onCreateVariable())); 48 | 49 | // 选中一列 50 | connect(horizontalHeader(),SIGNAL(sectionClicked(int)),this,SLOT(onColumnClicked(int))); 51 | 52 | // 选中一行 53 | connect(verticalHeader(),SIGNAL(sectionClicked(int)),this,SLOT(onRowClicked(int))); 54 | connect(horizontalHeader(),SIGNAL(customContextMenuRequested(QPoint)),this, SLOT(onMenuHorizontalHeaderRequested(QPoint))); 55 | connect(verticalHeader(),SIGNAL(customContextMenuRequested(QPoint)),this, SLOT(onMenuVerticalHeaderRequested(QPoint))); 56 | 57 | } 58 | 59 | void TableView::onMenuHorizontalHeaderRequested(QPoint pos) 60 | { 61 | Q_UNUSED(pos) 62 | qWarning() << "void TableView::onMenuHorizontalHeaderRequested(QPoint pos)"; 63 | m_popMenu->clear(); 64 | m_popMenu->addAction(m_actionCreate); 65 | m_popMenu->addAction(m_actionMod); 66 | m_popMenu->addAction(m_actionDel); 67 | m_popMenu->exec(QCursor::pos()); 68 | } 69 | 70 | void TableView::onMenuVerticalHeaderRequested(QPoint pos) 71 | { 72 | Q_UNUSED(pos) 73 | qWarning() << "void TableView::onMenuVerticalHeaderRequested(QPoint pos)"; 74 | m_popMenu->clear(); 75 | m_popMenu->addAction(m_actionCreate); 76 | m_popMenu->addAction(m_actionMod); 77 | m_popMenu->addAction(m_actionDel); 78 | m_popMenu->exec(QCursor::pos()); 79 | } 80 | 81 | void TableView::onColumnClicked(int nIndex) 82 | { 83 | setSelectionBehavior(QAbstractItemView::SelectColumns); 84 | QMessageBox msgBox; 85 | msgBox.setText(QStringLiteral("列单击")); 86 | msgBox.setInformativeText(QString::number(nIndex)); 87 | msgBox.setStandardButtons(QMessageBox::Ok|QMessageBox::Cancel); 88 | msgBox.setDefaultButton(QMessageBox::Ok); 89 | int ret = msgBox.exec(); 90 | } 91 | 92 | void TableView::onRowClicked(int nIndex) 93 | { 94 | setSelectionBehavior(QAbstractItemView::SelectRows); 95 | QMessageBox msgBox; 96 | msgBox.setText(QStringLiteral("行单击")); 97 | msgBox.setInformativeText(QString::number(nIndex)); 98 | msgBox.setStandardButtons(QMessageBox::Ok|QMessageBox::Cancel); 99 | msgBox.setDefaultButton(QMessageBox::Ok); 100 | int ret = msgBox.exec(); 101 | } 102 | 103 | void TableView::onCreateVariable() 104 | { 105 | QMessageBox msgBox; 106 | msgBox.setText(QStringLiteral("创建")); 107 | msgBox.setInformativeText(QStringLiteral("确定创建吗?")); 108 | msgBox.setStandardButtons(QMessageBox::Ok|QMessageBox::Cancel); 109 | msgBox.setDefaultButton(QMessageBox::Ok); 110 | int ret = msgBox.exec(); 111 | } 112 | 113 | void TableView::onDeleteVariable() 114 | { 115 | QMessageBox msgBox; 116 | msgBox.setText(QStringLiteral("删除")); 117 | msgBox.setInformativeText(QStringLiteral("确定删除吗?")); 118 | msgBox.setStandardButtons(QMessageBox::Ok|QMessageBox::Cancel); 119 | msgBox.setDefaultButton(QMessageBox::Ok); 120 | int ret = msgBox.exec(); 121 | } 122 | 123 | void TableView::onModifyVariable() 124 | { 125 | QMessageBox msgBox; 126 | msgBox.setText(QStringLiteral("修改")); 127 | msgBox.setInformativeText(QStringLiteral("确定修改吗?")); 128 | msgBox.setStandardButtons(QMessageBox::Ok|QMessageBox::Cancel); 129 | msgBox.setDefaultButton(QMessageBox::Ok); 130 | int ret = msgBox.exec(); 131 | } 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /src/TableView.h: -------------------------------------------------------------------------------- 1 | #ifndef TABLEVIEW_H 2 | #define TABLEVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class TableView : public QTableView 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TableView(QWidget *parent = 0); 14 | ~TableView(); 15 | 16 | /**< 初始化表格,自定义横向表头 */ 17 | void initHeaderView(); 18 | /**< 添加表头菜单 */ 19 | void initAction(); 20 | 21 | public slots: 22 | ////< 事件可继承重载 23 | virtual void onMenuHorizontalHeaderRequested(QPoint pos); 24 | virtual void onMenuVerticalHeaderRequested(QPoint pos); 25 | 26 | virtual void onColumnClicked(int nIndex); 27 | virtual void onRowClicked(int nIndex); 28 | 29 | virtual void onCreateVariable(); 30 | virtual void onDeleteVariable(); 31 | virtual void onModifyVariable(); 32 | 33 | private: 34 | QMenu *m_popMenu; ////< 菜单 35 | QAction *m_actionDel; ////< 删除 36 | QAction *m_actionMod; ////< 修改 37 | QAction *m_actionCreate; ////< 新建 38 | 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /src/VHeaderModel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | //#include "NodeManager.h" 5 | #include "VHeaderModel.h" 6 | //#include "DataNode.h" 7 | //#include "Const.h" 8 | #include "VHeaderView.h" 9 | 10 | VHeaderModel::VHeaderModel(QObject *parent) : QAbstractItemModel(parent) 11 | { 12 | } 13 | 14 | VHeaderModel::~VHeaderModel() 15 | { 16 | } 17 | 18 | QModelIndex VHeaderModel::index(int row, int column, const QModelIndex &parent) const 19 | { 20 | Q_UNUSED(parent); 21 | if (row < 0 || column < 0 /*|| NodeManager::getInstance()->sortMap().isEmpty()*/) 22 | return QModelIndex(); 23 | 24 | //QMap::const_iterator iterRow = NodeManager::getInstance()->sortMap().find(row); 25 | // if (iterRow == NodeManager::getInstance()->sortMap().end()) 26 | // return QModelIndex(); 27 | 28 | return createIndex(row, column); 29 | } 30 | 31 | QModelIndex VHeaderModel::parent(const QModelIndex &child) const 32 | { 33 | Q_UNUSED(child); 34 | return QModelIndex(); 35 | } 36 | 37 | int VHeaderModel::rowCount(const QModelIndex &parent) const 38 | { 39 | Q_UNUSED(parent); 40 | //return NodeManager::getInstance()->sortMap().size(); 41 | return 2; 42 | } 43 | 44 | int VHeaderModel::columnCount(const QModelIndex &parent) const 45 | { 46 | return 1; 47 | } 48 | 49 | QVariant VHeaderModel::data(const QModelIndex &index, int role) const 50 | { 51 | return QVariant(); 52 | } 53 | 54 | QVariant VHeaderModel::headerData(int section, Qt::Orientation orientation, int role) const 55 | { 56 | if (role == Qt::SizeHintRole) 57 | { 58 | QFontMetrics fonMetrics(VHeaderView::_Instance->font()); 59 | //int size = NodeManager::getInstance()->sortMap().size(); 60 | int size = 30; 61 | int width = fonMetrics.width(QString::number(size)); 62 | return QSize(width + 13, 0); //可以设定头的宽度,高度是默认的30,如果需要设置,则要重载相关函数,和delegate中实现,还没实现。 63 | } 64 | 65 | if (role != Qt::DisplayRole) 66 | return QVariant(); 67 | 68 | if (orientation == Qt::Vertical) 69 | { 70 | return QString::number(section); 71 | // DataNode* node = NodeManager::getInstance()->sortMap()[section]; 72 | // if (NULL != node) 73 | // { 74 | // int serial = NodeManager::getInstance()->serialNoOfNode(node); 75 | // return QString::number(serial+1); 76 | // } 77 | } 78 | return QVariant(); 79 | } 80 | 81 | /*Qt::ItemFlags VHeaderModel::flags(const QModelIndex &index) const 82 | { 83 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 84 | }*/ 85 | -------------------------------------------------------------------------------- /src/VHeaderModel.h: -------------------------------------------------------------------------------- 1 | #ifndef VHEADERMODEL_H 2 | #define VHEADERMODEL_H 3 | 4 | #include 5 | 6 | class DataNode; 7 | 8 | class VHeaderModel : public QAbstractItemModel 9 | { 10 | Q_OBJECT 11 | public: 12 | VHeaderModel(QObject *parent = 0); 13 | ~VHeaderModel(); 14 | 15 | virtual QModelIndex index(int row, int column, 16 | const QModelIndex &parent) const; 17 | virtual QModelIndex parent(const QModelIndex &child) const; 18 | virtual int rowCount(const QModelIndex &parent) const; 19 | virtual int columnCount(const QModelIndex &parent) const; 20 | virtual QVariant data(const QModelIndex &index, int role) const; 21 | //virtual Qt::ItemFlags flags(const QModelIndex &index) const; 22 | 23 | virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; 24 | 25 | private: 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/VHeaderView.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "VHeaderView.h" 5 | //#include "DataTreeView.h" 6 | 7 | VHeaderView* VHeaderView::_Instance = NULL; 8 | 9 | VHeaderView::VHeaderView(Qt::Orientation orientation, QWidget * parent) : QHeaderView(orientation, parent) 10 | { 11 | _Instance = this; 12 | //this->setResizeMode(QHeaderView::Fixed); 13 | _selectRow = -1; 14 | } 15 | 16 | VHeaderView::~VHeaderView() 17 | { 18 | } 19 | 20 | void VHeaderView::mousePressEvent ( QMouseEvent * event ) 21 | { 22 | //获取当前点击的行号 23 | int row = this->logicalIndexAt (event->pos()); 24 | 25 | //保存行号 26 | _selectRow = row; 27 | 28 | //获取当前点中的树形节点的index 29 | // QModelIndex selIndex = DataTreeView::_Instance->rowIndex(row); 30 | // if (!selIndex.isValid()) 31 | // return; 32 | 33 | //获取树形的选择模型 34 | // QItemSelectionModel* selModel = DataTreeView::_Instance->selectionModel(); 35 | // if (NULL == selModel) 36 | // return; 37 | 38 | //清空之前选中的 39 | // selModel->clear(); 40 | 41 | //选中当前行 42 | // selModel->setCurrentIndex(selIndex, QItemSelectionModel::Rows | QItemSelectionModel::Select); 43 | 44 | //需要设置为焦点,才能高亮显示 45 | //DataTreeView::_Instance->setFocus(); 46 | } 47 | 48 | //轻量级的重载(相对paintevent) 重画每一列 49 | void VHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const 50 | { 51 | //先调用默认的处理 52 | painter->save(); 53 | QHeaderView::paintSection(painter, rect, logicalIndex); 54 | painter->restore(); 55 | 56 | //判断是否选中,如果选中了当前行,则要画一个三角符号 57 | if (_selectRow == logicalIndex) 58 | { 59 | drawPreBtn(rect, painter); 60 | viewport()->update(); 61 | } 62 | } 63 | 64 | void VHeaderView::drawPreBtn(const QRect& rect, QPainter* painter) const 65 | { 66 | QRect brect = QRect(rect.right() - 13, rect.center().y() - 6, 13, 13); 67 | 68 | painter->save(); 69 | painter->setPen(QColor(71, 71, 71)); 70 | painter->drawLine(brect.right() - 4, brect.center().y() - 3, brect.right() - 4, brect.center().y() + 3); 71 | painter->drawLine(brect.right() - 3, brect.center().y() - 2, brect.right() - 3, brect.center().y() + 2); 72 | painter->drawLine(brect.right() - 2, brect.center().y() - 1, brect.right() - 2, brect.center().y() + 1); 73 | painter->drawPoint(brect.right() - 1, brect.center().y()); 74 | painter->restore(); 75 | } 76 | 77 | void VHeaderView::selectRow(int row) 78 | { 79 | _selectRow = row; 80 | viewport()->update(); 81 | } 82 | -------------------------------------------------------------------------------- /src/VHeaderView.h: -------------------------------------------------------------------------------- 1 | #ifndef VHEADERVIEW_H 2 | #define VHEADERVIEW_H 3 | 4 | #include 5 | 6 | class VHeaderView : public QHeaderView 7 | { 8 | Q_OBJECT 9 | public: 10 | VHeaderView(Qt::Orientation orientation, QWidget * parent = 0); 11 | ~VHeaderView(); 12 | static VHeaderView* _Instance; 13 | 14 | void selectRow(int row); 15 | protected: 16 | virtual void mousePressEvent ( QMouseEvent * event ); 17 | virtual void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; 18 | 19 | private: 20 | void drawPreBtn(const QRect& rect, QPainter* painter) const; 21 | 22 | private: 23 | int _selectRow; //保存选中的行 24 | }; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "MainWidget.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWidget w; 8 | w.resize(800,500); 9 | w.show(); 10 | 11 | return a.exec(); 12 | } 13 | --------------------------------------------------------------------------------