├── DragTabWidget ├── DragTabBar.hpp ├── DragTabWidget.hpp ├── TabDialog.hpp └── TabPageWidget.hpp ├── FilterWidget.hpp ├── LICENSE ├── Model ├── FrozenTableView.hpp ├── XButtonDelegate.hpp ├── XComboBoxDelegate.hpp ├── XHeaderView.hpp ├── XHeaderViewTest.hpp ├── XPersistentItemDelegate.hpp ├── XProgressDelegate.hpp ├── XSortFilterProxyModel.hpp ├── XTableModel.hpp ├── XTableModelItem.hpp ├── YButtonDelegate.hpp ├── YHeaderView.hpp └── YHeaderViewTest.hpp ├── QCustomPlot ├── qcustomplot.cpp └── qcustomplot.h ├── README.md └── WindowBase.hpp /DragTabWidget/DragTabBar.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAGTABBAR_HPP 2 | #define DRAGTABBAR_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace FinTechUI{ 11 | 12 | class DragTabBar : public QTabBar 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit DragTabBar(QWidget* parent = NULL): QTabBar(parent) 17 | { 18 | setAttribute(Qt::WA_StyledBackground); 19 | setMovable(true); 20 | } 21 | protected: 22 | void mousePressEvent(QMouseEvent* event)override 23 | { 24 | QTabBar::mousePressEvent(event); 25 | if(event->button() == Qt::LeftButton && currentIndex() >= 0) 26 | { 27 | m_bDrag = true; 28 | } 29 | } 30 | 31 | void mouseMoveEvent(QMouseEvent* event)override 32 | { 33 | QTabBar::mouseMoveEvent(event); 34 | if(m_bDrag && event->buttons()) 35 | { 36 | if(!m_bDragOut && !contentsRect().contains(event->pos())) 37 | { 38 | m_bDragOut = true; 39 | emit DragOut(this->currentIndex()); 40 | } 41 | } 42 | } 43 | 44 | void mouseReleaseEvent(QMouseEvent* event)override 45 | { 46 | m_bDrag = false; 47 | m_bDragOut = false; 48 | QTabBar::mouseReleaseEvent(event); 49 | } 50 | signals: 51 | void DragOut(int index); 52 | protected: 53 | bool m_bDrag; 54 | bool m_bDragOut; 55 | }; 56 | } 57 | 58 | #endif // DRAGTABBAR_HPP 59 | -------------------------------------------------------------------------------- /DragTabWidget/DragTabWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DRAGTABWIDGET_HPP 2 | #define DRAGTABWIDGET_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "DragTabBar.hpp" 15 | #include "TabDialog.hpp" 16 | #include "TabPageWidget.hpp" 17 | 18 | namespace FinTechUI{ 19 | 20 | class DragTabWidget : public QTabWidget 21 | { 22 | Q_OBJECT 23 | public: 24 | explicit DragTabWidget(QWidget *parent = NULL): QTabWidget(parent) 25 | { 26 | setAttribute(Qt::WA_StyledBackground); 27 | initTabBar(); 28 | connect(this, SIGNAL(PopWindow(TabPageWidget*)), this, SLOT(OnShowTabDialog(TabPageWidget*))); 29 | connect(m_tabBar, SIGNAL(DragOut(int)), this, SLOT(OnDragOut(int))); 30 | } 31 | 32 | int addTabPage(TabPageWidget *page, const QString& title) 33 | { 34 | int index = -1; 35 | TabPageWidget* tabPage = static_cast(page); 36 | if(tabPage != NULL) 37 | { 38 | const int index = addTab(tabPage, title); 39 | tabPage->setTabLabel(title); 40 | setCurrentIndex(index); 41 | } 42 | if(!m_tabPages.contains(page)) 43 | { 44 | m_tabPages.append(tabPage); 45 | } 46 | 47 | return index; 48 | } 49 | 50 | void removeTabPage(TabPageWidget *page) 51 | { 52 | if(page != NULL) 53 | { 54 | removeTab(indexOf(page)); 55 | } 56 | } 57 | 58 | void takeTabPage(TabPageWidget *page) 59 | { 60 | if(page != NULL) 61 | { 62 | removeTab(indexOf(page)); 63 | } 64 | } 65 | 66 | void removeTabPage(int index) 67 | { 68 | if(indexValid(index)) 69 | { 70 | removeTab(index); 71 | } 72 | } 73 | 74 | void removeCurrentPage() 75 | { 76 | removeTab(currentIndex()); 77 | } 78 | 79 | bool indexValid(int index) const 80 | { 81 | if(index < 0 || index >= this->count()) 82 | return false; 83 | return true; 84 | } 85 | 86 | bool pageValid(TabPageWidget *page) const 87 | { 88 | if(page == NULL) 89 | return false; 90 | return indexValid(indexOf(page)); 91 | } 92 | protected: 93 | void initTabBar() 94 | { 95 | m_tabBar = new DragTabBar(this); 96 | setTabBar(m_tabBar); 97 | } 98 | 99 | void popPage(TabPageWidget *page) 100 | { 101 | TabPageWidget* tabPage = dynamic_cast(page); 102 | if(tabPage != NULL) 103 | { 104 | takeTabPage(tabPage); 105 | TabDialog* pop = new TabDialog(); 106 | pop->setAttribute(Qt::WA_DeleteOnClose); 107 | pop->setContentWidget(tabPage); 108 | pop->setWindowTitle(tabPage->tabLabel()); 109 | pop->resize(page->size()); 110 | pop->move(QCursor::pos() - QPoint(10, 10)); 111 | connect(pop, SIGNAL(DragInTabPage(QDialog*)), this, SLOT(OnDragInTabPage(QDialog*))); 112 | pop->show(); 113 | pop->activateWindow(); 114 | pop->setFocus(); 115 | } 116 | 117 | } 118 | 119 | signals: 120 | void PopWindow(TabPageWidget* page); 121 | protected slots: 122 | void OnDragOut(int index) 123 | { 124 | if(!indexValid(index)) 125 | return; 126 | if(count() <= 1) 127 | return; 128 | QWidget* tabPage = this->widget(index); 129 | if(tabPage == NULL || !m_tabPages.contains(tabPage)) 130 | return; 131 | QMimeData *mime = new QMimeData; 132 | QDrag *drag = new QDrag(m_tabBar); 133 | QVariant data; 134 | data.setValue(tabPage); 135 | drag->setProperty("page", data); 136 | drag->setMimeData(mime); 137 | drag->setHotSpot(QPoint(10, 0)); 138 | connect(drag, SIGNAL(destroyed(QObject*)), this, SLOT(OnDragRelease(QObject*))); 139 | drag->exec(Qt::MoveAction); 140 | } 141 | 142 | void OnShowTabDialog(TabPageWidget* page) 143 | { 144 | QPoint bar_point = m_tabBar->mapFromGlobal(QCursor::pos()); 145 | if(!m_tabBar->contentsRect().contains(bar_point)) 146 | { 147 | popPage(page); 148 | } 149 | } 150 | 151 | void OnDragRelease(QObject* obj) 152 | { 153 | if(obj != NULL) 154 | { 155 | QVariant data = obj->property("page"); 156 | TabPageWidget* page = reinterpret_cast(data.value()); 157 | if(page != NULL) 158 | { 159 | emit PopWindow(page); 160 | } 161 | } 162 | } 163 | 164 | void OnDragInTabPage(QDialog* tabDialog) 165 | { 166 | TabDialog* tabPage = dynamic_cast(tabDialog); 167 | if(tabPage != NULL) 168 | { 169 | TabPageWidget* page = static_cast(tabPage->getContentWidget()); 170 | addTabPage(page, page->tabLabel()); 171 | tabDialog->disconnect(); 172 | tabDialog->close(); 173 | } 174 | } 175 | protected: 176 | QList m_tabPages; 177 | QTabBar* m_tabBar; 178 | }; 179 | } 180 | #endif // DRAGTABWIDGET_HPP 181 | -------------------------------------------------------------------------------- /DragTabWidget/TabDialog.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TABDIALOG_HPP 2 | #define TABDIALOG_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "TabPageWidget.hpp" 10 | 11 | namespace FinTechUI{ 12 | class TabDialog : public QDialog 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit TabDialog(QWidget* parent = NULL): QDialog(parent) 17 | { 18 | m_tabPage = NULL; 19 | } 20 | 21 | void setContentWidget(TabPageWidget* page) 22 | { 23 | TabPageWidget* tabPage = dynamic_cast(page); 24 | if(tabPage != NULL) 25 | { 26 | m_tabPage = tabPage; 27 | QVBoxLayout* layout = new QVBoxLayout(); 28 | layout->setMargin(0); 29 | layout->addWidget(m_tabPage); 30 | setLayout(layout); 31 | setWindowTitle(tabPage->tabLabel()); 32 | Qt::WindowFlags flags = Qt::Dialog; 33 | flags |= Qt::WindowMinMaxButtonsHint; 34 | flags |= Qt::WindowCloseButtonHint; 35 | setWindowFlags(flags); 36 | m_tabPage->show(); 37 | } 38 | } 39 | 40 | QWidget* getContentWidget()const 41 | { 42 | return m_tabPage; 43 | } 44 | protected: 45 | bool event(QEvent *event) 46 | { 47 | switch(event->type()) 48 | { 49 | // close Dialog 50 | case QEvent::Close: 51 | // restore TabPage to TabWidget 52 | emit DragInTabPage(this); 53 | break; 54 | // double click title bar 55 | case QEvent::NonClientAreaMouseButtonDblClick: 56 | showMaximized(); 57 | break; 58 | } 59 | return QDialog::event(event); 60 | } 61 | void keyPressEvent(QKeyEvent *event) 62 | { 63 | switch(event->type()) 64 | { 65 | case Qt::Key_Escape: 66 | 67 | break; 68 | default: 69 | QDialog::keyPressEvent(event); 70 | break; 71 | } 72 | } 73 | protected slots: 74 | void reject() 75 | { 76 | emit DragInTabPage(this); 77 | QDialog::reject(); 78 | } 79 | signals: 80 | void DragRelease(const QPoint& globalPos, QDialog*); 81 | void DragInTabPage(QDialog*); 82 | private: 83 | TabPageWidget* m_tabPage; 84 | }; 85 | } 86 | 87 | #endif // TABDIALOG_HPP 88 | -------------------------------------------------------------------------------- /DragTabWidget/TabPageWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TABPAGEWIDGET_HPP 2 | #define TABPAGEWIDGET_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace FinTechUI{ 8 | class TabPageWidget : public QScrollArea 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TabPageWidget(QWidget *parent = NULL) : QScrollArea(parent) 13 | { 14 | setWidgetResizable(true); 15 | } 16 | 17 | void setTabLabel(const QString& tab) 18 | { 19 | this->tab = tab; 20 | } 21 | 22 | QString tabLabel()const 23 | { 24 | return tab; 25 | } 26 | protected: 27 | QString tab; 28 | }; 29 | } 30 | 31 | #endif // TABPAGEWIDGET_HPP 32 | -------------------------------------------------------------------------------- /FilterWidget.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FILTERWIDGET_HPP 2 | #define FILTERWIDGET_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace FinTechUI 19 | { 20 | 21 | class FilterWidget : public QWidget 22 | { 23 | Q_OBJECT 24 | public: 25 | explicit FilterWidget(QWidget *parent = NULL): QWidget(parent) 26 | { 27 | 28 | } 29 | 30 | void SetHeaderLabels(const QStringList& labels) 31 | { 32 | m_HeaderLabels = labels; 33 | QHBoxLayout* layout = new QHBoxLayout; 34 | layout->setContentsMargins(0, 0, 0, 0); 35 | layout->setSpacing(0); 36 | QFont serifFont("Times", 10, QFont::Bold); 37 | int i = 0; 38 | foreach(QString label, m_HeaderLabels) 39 | { 40 | QVBoxLayout* vLayout = new QVBoxLayout; 41 | vLayout->setContentsMargins(0, 0, 0, 0); 42 | vLayout->setSpacing(0); 43 | QLabel* labelWidget = new QLabel(label); 44 | labelWidget->setFixedHeight(25); 45 | labelWidget->setAlignment(Qt::AlignCenter); 46 | labelWidget->setFont(serifFont); 47 | vLayout->addWidget(labelWidget); 48 | QTableWidget* listWidget = new QTableWidget(0, 1); 49 | listWidget->horizontalHeader()->hide(); 50 | listWidget->verticalHeader()->hide(); 51 | listWidget->setShowGrid(true); 52 | listWidget->setObjectName(label); 53 | listWidget->setSelectionBehavior(QAbstractItemView::SelectRows); 54 | listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); 55 | listWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); 56 | listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 57 | listWidget->horizontalHeader()->setStretchLastSection(true); 58 | listWidget->setAlternatingRowColors(true); 59 | vLayout->addWidget(listWidget); 60 | m_ListWidgetMap[label] = listWidget; 61 | layout->addLayout(vLayout); 62 | connect(listWidget, &QTableWidget::cellClicked, this, &FilterWidget::OnClickedFilter, Qt::UniqueConnection); 63 | m_IndexLabelMap[i] = label; 64 | m_LabelSelectedRowMap[label] = -1; 65 | m_LabelIndexMap[label] = i++; 66 | } 67 | layout->addStretch(1); 68 | setLayout(layout); 69 | } 70 | 71 | void SetData(const QString& label, const QStringList& items) 72 | { 73 | if(m_ListWidgetMap.contains(label)) 74 | { 75 | QStringList list = items; 76 | list.sort(); 77 | m_DataMap[label] = list; 78 | QTableWidget* listWidget = m_ListWidgetMap[label]; 79 | while(listWidget->rowCount() > 0) 80 | { 81 | listWidget->removeRow(0); 82 | } 83 | int row = listWidget->rowCount(); 84 | listWidget->insertRow(row); 85 | QTableWidgetItem* allItem = new QTableWidgetItem; 86 | allItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignLeft); 87 | allItem->setText("All"); 88 | listWidget->setItem(row++, 0, allItem); 89 | 90 | foreach (QString item, list) 91 | { 92 | listWidget->insertRow(row); 93 | QTableWidgetItem* dataItem = new QTableWidgetItem; 94 | dataItem->setTextAlignment(Qt::AlignVCenter | Qt::AlignLeft); 95 | dataItem->setText(item); 96 | listWidget->setItem(row++, 0, dataItem); 97 | } 98 | } 99 | } 100 | 101 | void SetColumnWidth(const QString& label, int width) 102 | { 103 | if(m_ListWidgetMap.contains(label)) 104 | { 105 | m_ListWidgetMap[label]->setFixedWidth(width); 106 | m_ListWidgetMap[label]->setColumnWidth(0, width); 107 | } 108 | int total = 0; 109 | foreach(QString label, m_ListWidgetMap.keys()) 110 | { 111 | total += m_ListWidgetMap[label]->width(); 112 | } 113 | setMaximumWidth(total); 114 | } 115 | 116 | void SetDataRelationMap(const QVector>& map) 117 | { 118 | m_DataRelationMap = map; 119 | for (int i = 0; i < map.size(); i++) 120 | { 121 | QString label = m_IndexLabelMap[i]; 122 | QStringList items = map.at(i).keys(); 123 | items.sort(); 124 | SetData(label, items); 125 | } 126 | QString label = m_IndexLabelMap[map.size()]; 127 | QStringList items; 128 | foreach (QString key, map.last().keys()) 129 | { 130 | items.append(map.last().value(key)); 131 | } 132 | items.removeDuplicates(); 133 | items.sort(); 134 | SetData(label, items); 135 | } 136 | 137 | protected slots: 138 | void OnClickedFilter(int row) 139 | { 140 | QTableWidget* listWidget = dynamic_cast(QObject::sender()); 141 | if(listWidget != NULL) 142 | { 143 | QString label = listWidget->objectName(); 144 | if(m_LabelSelectedRowMap[label] == row) 145 | { 146 | m_LabelSelectedRowMap[label] = -1; 147 | listWidget->clearSelection(); 148 | } 149 | else 150 | { 151 | m_LabelSelectedRowMap[label] = row; 152 | if(row == 0) 153 | { 154 | listWidget->selectAll(); 155 | } 156 | else 157 | { 158 | QList selectedItems = m_ListWidgetMap[label]->selectedItems(); 159 | if(selectedItems.size() == (m_ListWidgetMap[label]->rowCount() - 1) && !selectedItems.contains(m_ListWidgetMap[label]->item(0, 0))) 160 | { 161 | listWidget->selectAll(); 162 | } 163 | } 164 | } 165 | 166 | int count = m_LabelIndexMap.size(); 167 | for (int i = m_LabelIndexMap[label] + 1; i < count; i++) 168 | { 169 | QString Key = m_IndexLabelMap[i]; 170 | m_ListWidgetMap[Key]->clearSelection(); 171 | } 172 | QVector filter; 173 | for (int i = 0; i < count; i++) 174 | { 175 | QString Key = m_IndexLabelMap[i]; 176 | QList selectedItems = m_ListWidgetMap[Key]->selectedItems(); 177 | QStringList pattern; 178 | foreach(QTableWidgetItem* item, selectedItems) 179 | { 180 | if(item->text() != "All") 181 | { 182 | pattern.append(item->text()); 183 | } 184 | } 185 | if(pattern.size() == 0) 186 | { 187 | pattern = m_DataMap[Key]; 188 | } 189 | filter.push_back(pattern); 190 | } 191 | QVector newFilter; 192 | count = m_LabelIndexMap.size(); 193 | QStringList nextPattern; 194 | for (int i = 0; i < count; i++) 195 | { 196 | if(i < m_LabelIndexMap[label]) 197 | { 198 | newFilter.push_back(filter.at(i)); 199 | } 200 | else 201 | { 202 | QStringList curPattern; 203 | if(i == m_LabelIndexMap[label]) 204 | { 205 | curPattern = filter.at(i); 206 | } 207 | else 208 | { 209 | curPattern = nextPattern; 210 | nextPattern.clear(); 211 | } 212 | newFilter.push_back(curPattern); 213 | if(i < count - 1) 214 | { 215 | QMap map = m_DataRelationMap.at(i); 216 | foreach (QString key, newFilter.last()) 217 | { 218 | nextPattern.append(map[key]); 219 | } 220 | nextPattern.removeDuplicates(); 221 | nextPattern.sort(); 222 | SetData(m_IndexLabelMap[i + 1], nextPattern); 223 | } 224 | } 225 | } 226 | emit FilterChanged(newFilter); 227 | } 228 | } 229 | signals: 230 | void FilterChanged(const QVector& filter); 231 | 232 | protected: 233 | QStringList m_HeaderLabels; 234 | QMap m_DataMap; 235 | QMap m_LabelIndexMap; 236 | QMap m_IndexLabelMap; 237 | QMap m_ListWidgetMap; 238 | QMap m_LabelSelectedRowMap; 239 | QVector> m_DataRelationMap; 240 | }; 241 | 242 | } 243 | 244 | #endif // FILTERWIDGET_HPP 245 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 QuantFabric 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Model/FrozenTableView.hpp: -------------------------------------------------------------------------------- 1 | #ifndef FROZENTABLEVIEW_H 2 | #define FROZENTABLEVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace FinTechUI 13 | { 14 | 15 | class FrozenTableView : public QTableView 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit FrozenTableView(unsigned int frozenColumns, QWidget* parent = NULL) : QTableView(parent) 20 | { 21 | m_frozenColumns = frozenColumns; 22 | InitFrozenTableView(); 23 | InitTableView(); 24 | } 25 | 26 | virtual ~FrozenTableView() 27 | { 28 | delete m_frozenTableView; 29 | m_frozenTableView = NULL; 30 | } 31 | 32 | virtual void setModel(QAbstractItemModel *model) 33 | { 34 | if(model == NULL) 35 | return; 36 | QTableView::setModel(model); 37 | if(m_frozenTableView != NULL) 38 | { 39 | m_frozenTableView->setModel(model); 40 | m_frozenTableView->setSelectionModel(selectionModel()); 41 | // 隐藏冻结表格的后续列、显示表格前m_frozenColumns冻结列,确保表格行对齐 42 | for(int col = 0; col < m_frozenColumns; col++) 43 | { 44 | m_frozenTableView->setColumnWidth(col, columnWidth(col)); 45 | } 46 | for(int col = m_frozenColumns; col < model->columnCount(); col++) 47 | { 48 | m_frozenTableView->setColumnHidden(col, true); 49 | } 50 | } 51 | } 52 | 53 | virtual void setColumnHidden(int col, bool hidden) 54 | { 55 | if(m_frozenTableView != NULL && col < m_frozenColumns) 56 | { 57 | m_frozenTableView->setColumnHidden(col, hidden); 58 | } 59 | QTableView::setColumnHidden(col, hidden); 60 | } 61 | 62 | virtual void setColumnWidth(int col, int width) 63 | { 64 | if(m_frozenTableView != NULL && col < m_frozenColumns) 65 | { 66 | m_frozenTableView->setColumnWidth(col, width); 67 | } 68 | QTableView::setColumnWidth(col, width); 69 | } 70 | 71 | virtual void setShowGrid(bool show) 72 | { 73 | if(m_frozenTableView != NULL) 74 | { 75 | m_frozenTableView->setShowGrid(show); 76 | } 77 | QTableView::setShowGrid(show); 78 | } 79 | 80 | virtual void setHorizontalHeader(QHeaderView *header) 81 | { 82 | if(header == NULL) 83 | return; 84 | if(m_frozenTableView != NULL) 85 | { 86 | m_frozenTableView->setHorizontalHeader(header); 87 | } 88 | QTableView::setHorizontalHeader(header); 89 | } 90 | 91 | virtual QModelIndex indexAt(const QPoint &pos) const 92 | { 93 | if(underMouse()) 94 | { 95 | return QTableView::indexAt(pos); 96 | } 97 | if(m_frozenTableView != NULL) 98 | { 99 | if(m_frozenTableView->underMouse()) 100 | { 101 | return m_frozenTableView->indexAt(pos); 102 | } 103 | } 104 | return QModelIndex(); 105 | } 106 | 107 | virtual void setRowHeight(int row, int height) 108 | { 109 | if(m_frozenTableView != NULL) 110 | { 111 | m_frozenTableView->setRowHeight(row, height); 112 | } 113 | QTableView::setRowHeight(row, height); 114 | } 115 | 116 | virtual void setRowHidden(int row, bool hide) 117 | { 118 | if(m_frozenTableView != NULL) 119 | { 120 | m_frozenTableView->setRowHidden(row, hide); 121 | } 122 | QTableView::setRowHidden(row, hide); 123 | } 124 | 125 | virtual void setSortingEnabled(bool enable) 126 | { 127 | if(m_frozenTableView != NULL) 128 | { 129 | m_frozenTableView->setSortingEnabled(enable); 130 | } 131 | QTableView::setSortingEnabled(enable); 132 | } 133 | 134 | virtual void setEditTriggers(QAbstractItemView::EditTriggers triggers) 135 | { 136 | if(m_frozenTableView != NULL) 137 | { 138 | m_frozenTableView->setEditTriggers(triggers); 139 | } 140 | QTableView::setEditTriggers(triggers); 141 | } 142 | 143 | virtual void setSelectionBehavior(QAbstractItemView::SelectionBehavior behavior) 144 | { 145 | if(m_frozenTableView != NULL) 146 | { 147 | m_frozenTableView->setSelectionBehavior(behavior); 148 | } 149 | QTableView::setSelectionBehavior(behavior); 150 | } 151 | 152 | virtual void setSelectionMode(QAbstractItemView::SelectionMode mode) 153 | { 154 | if(m_frozenTableView != NULL) 155 | { 156 | m_frozenTableView->setSelectionMode(mode); 157 | } 158 | QTableView::setSelectionMode(mode); 159 | } 160 | 161 | virtual void sortByColumn(int column, Qt::SortOrder order) 162 | { 163 | if(m_frozenTableView != NULL && column < m_frozenColumns) 164 | { 165 | m_frozenTableView->sortByColumn(column, order); 166 | } 167 | QTableView::sortByColumn(column, order); 168 | } 169 | 170 | virtual void hideColumn(int column) 171 | { 172 | if(m_frozenTableView != NULL && column < m_frozenColumns) 173 | { 174 | m_frozenTableView->hideColumn(column); 175 | } 176 | QTableView::hideColumn(column); 177 | } 178 | 179 | virtual void hideRow(int row) 180 | { 181 | if(m_frozenTableView != NULL) 182 | { 183 | m_frozenTableView->hideRow(row); 184 | } 185 | QTableView::hideRow(row); 186 | } 187 | 188 | virtual void resizeColumnToContents(int column) 189 | { 190 | if(m_frozenTableView != NULL && column < m_frozenColumns) 191 | { 192 | m_frozenTableView->resizeColumnToContents(column); 193 | } 194 | QTableView::resizeColumnToContents(column); 195 | } 196 | 197 | virtual void resizeColumnsToContents() 198 | { 199 | if(m_frozenTableView != NULL) 200 | { 201 | m_frozenTableView->resizeColumnsToContents(); 202 | } 203 | QTableView::resizeColumnsToContents(); 204 | } 205 | 206 | virtual void resizeRowToContents(int row) 207 | { 208 | if(m_frozenTableView != NULL) 209 | { 210 | m_frozenTableView->resizeRowToContents(row); 211 | } 212 | QTableView::resizeRowToContents(row); 213 | } 214 | 215 | virtual void resizeRowsToContents() 216 | { 217 | if(m_frozenTableView != NULL) 218 | { 219 | m_frozenTableView->resizeRowsToContents(); 220 | } 221 | QTableView::resizeRowsToContents(); 222 | } 223 | 224 | virtual void selectColumn(int column) 225 | { 226 | if(m_frozenTableView != NULL && column < m_frozenColumns) 227 | { 228 | m_frozenTableView->selectColumn(column); 229 | } 230 | QTableView::selectColumn(column); 231 | } 232 | 233 | virtual void selectRow(int row) 234 | { 235 | if(m_frozenTableView != NULL) 236 | { 237 | m_frozenTableView->selectRow(row); 238 | } 239 | QTableView::selectRow(row); 240 | } 241 | 242 | virtual void showColumn(int column) 243 | { 244 | if(m_frozenTableView != NULL && column < m_frozenColumns) 245 | { 246 | m_frozenTableView->showColumn(column); 247 | } 248 | QTableView::showColumn(column); 249 | } 250 | 251 | virtual void showRow(int row) 252 | { 253 | if(m_frozenTableView != NULL) 254 | { 255 | m_frozenTableView->showRow(row); 256 | } 257 | QTableView::showRow(row); 258 | } 259 | protected: 260 | void InitTableView() 261 | { 262 | QTableView::setEditTriggers(QAbstractItemView::NoEditTriggers); 263 | QTableView::setSelectionBehavior(QAbstractItemView::SelectRows); 264 | QTableView::setSelectionMode(QAbstractItemView::SingleSelection); 265 | QTableView::setAlternatingRowColors(true); 266 | QTableView::setSortingEnabled(true); 267 | horizontalHeader()->setStretchLastSection(true); 268 | verticalHeader()->hide(); 269 | setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); 270 | setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); 271 | connect(this, &QAbstractItemView::doubleClicked, this, &FrozenTableView::doubleClicked); 272 | connect(this, &QAbstractItemView::clicked, this, &FrozenTableView::clicked); 273 | } 274 | 275 | void InitFrozenTableView() 276 | { 277 | if(m_frozenColumns > 0) 278 | { 279 | m_frozenTableView = new QTableView(this); 280 | m_frozenTableView->setFocusPolicy(Qt::NoFocus); 281 | // 设置冻结表格为无边框模式,确保没有边框重叠 282 | m_frozenTableView->setFrameShape(QFrame::NoFrame); 283 | m_frozenTableView->verticalHeader()->hide(); 284 | m_frozenTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed); 285 | m_frozenTableView->horizontalHeader()->setStretchLastSection(true); 286 | m_frozenTableView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 287 | m_frozenTableView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 288 | m_frozenTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); 289 | m_frozenTableView->setEditTriggers(QAbstractItemView::NoEditTriggers); 290 | m_frozenTableView->setSelectionBehavior(QAbstractItemView::SelectRows); 291 | m_frozenTableView->setSelectionMode(QAbstractItemView::SingleSelection); 292 | m_frozenTableView->setAlternatingRowColors(true); 293 | m_frozenTableView->setSortingEnabled(true); 294 | m_frozenTableView->horizontalHeader()->setSortIndicatorShown(false); 295 | 296 | viewport()->stackUnder(m_frozenTableView); 297 | UpdateFrozenTableGeometry(); 298 | 299 | //connect the headers and scrollbars of both tableviews together 300 | connect(horizontalHeader(),&QHeaderView::sectionResized, this, 301 | &FrozenTableView::OnUpdateSectionWidth); 302 | connect(verticalHeader(),&QHeaderView::sectionResized, this, 303 | &FrozenTableView::OnUpdateSectionHeight); 304 | 305 | connect(m_frozenTableView->horizontalHeader(),&QHeaderView::sectionClicked, this, 306 | &FrozenTableView::OnSectionClicked); 307 | connect(horizontalHeader(),&QHeaderView::sectionClicked, this, 308 | &FrozenTableView::OnSectionClicked); 309 | 310 | connect(m_frozenTableView->verticalScrollBar(), &QAbstractSlider::valueChanged, 311 | verticalScrollBar(), &QAbstractSlider::setValue); 312 | connect(verticalScrollBar(), &QAbstractSlider::valueChanged, 313 | m_frozenTableView->verticalScrollBar(), &QAbstractSlider::setValue); 314 | connect(m_frozenTableView, &QAbstractItemView::doubleClicked, this, &FrozenTableView::doubleClicked); 315 | connect(m_frozenTableView, &QAbstractItemView::clicked, this, &FrozenTableView::clicked); 316 | } 317 | else 318 | { 319 | m_frozenTableView = NULL; 320 | } 321 | } 322 | 323 | void UpdateFrozenTableGeometry() 324 | { 325 | int width = 0; 326 | for(int i = 0; i < m_frozenColumns; i++) 327 | { 328 | width += m_frozenTableView->columnWidth(i); 329 | } 330 | m_frozenTableView->setGeometry(verticalHeader()->width() + frameWidth(), 331 | frameWidth(), width, 332 | viewport()->height() + horizontalHeader()->height()); 333 | } 334 | 335 | public: 336 | virtual void resizeEvent(QResizeEvent *event) 337 | { 338 | QTableView::resizeEvent(event); 339 | UpdateFrozenTableGeometry(); 340 | } 341 | 342 | virtual QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) 343 | { 344 | QModelIndex current = QTableView::moveCursor(cursorAction, modifiers); 345 | int width = 0; 346 | for(int i = 0; i < m_frozenColumns; i++) 347 | { 348 | width += m_frozenTableView->columnWidth(i); 349 | } 350 | // 水平拖动时设置滚动条 351 | if (cursorAction == MoveLeft && current.column() >= m_frozenColumns && visualRect(current).topLeft().x() < width) 352 | { 353 | const int newValue = horizontalScrollBar()->value() + visualRect(current).topLeft().x() - width; 354 | horizontalScrollBar()->setValue(newValue); 355 | } 356 | return current; 357 | } 358 | 359 | virtual void scrollTo (const QModelIndex & index, ScrollHint hint = EnsureVisible) 360 | { 361 | if(m_frozenColumns == 0) 362 | { 363 | QTableView::scrollTo(index, hint); 364 | } 365 | else if(m_frozenTableView != NULL) 366 | { 367 | m_frozenTableView->scrollTo(index, hint); 368 | QTableView::scrollTo(index, hint); 369 | } 370 | } 371 | protected slots: 372 | void OnUpdateSectionWidth(int logicalIndex, int oldSize, int newSize) 373 | { 374 | if (logicalIndex == m_frozenColumns - 1) 375 | { 376 | // 设置冻结列前m_frozenColumns - 1列的固定宽度 377 | for(int i = 0; i < m_frozenColumns - 1; i++) 378 | { 379 | m_frozenTableView->setColumnWidth(i, columnWidth(i)); 380 | } 381 | // 根据拖动尺寸变化设置第m_frozenColumns - 1列 382 | m_frozenTableView->setColumnWidth(logicalIndex, newSize); 383 | UpdateFrozenTableGeometry(); 384 | } 385 | } 386 | 387 | void OnUpdateSectionHeight(int logicalIndex, int oldSize, int newSize) 388 | { 389 | m_frozenTableView->setRowHeight(logicalIndex, newSize); 390 | } 391 | 392 | void OnSectionClicked(int section) 393 | { 394 | if(m_frozenTableView != NULL) 395 | { 396 | if(section < m_frozenColumns) 397 | { 398 | horizontalHeader()->setSortIndicatorShown(false); 399 | m_frozenTableView->horizontalHeader()->setSortIndicatorShown(true); 400 | } 401 | else 402 | { 403 | horizontalHeader()->setSortIndicatorShown(true); 404 | m_frozenTableView->horizontalHeader()->setSortIndicatorShown(false); 405 | } 406 | } 407 | } 408 | signals: 409 | void doubleClicked(const QModelIndex &index); 410 | void clicked(const QModelIndex &index); 411 | protected: 412 | QTableView* m_frozenTableView; 413 | int m_frozenColumns; 414 | }; 415 | 416 | } 417 | 418 | 419 | #endif // FROZENTABLEVIEW_H 420 | -------------------------------------------------------------------------------- /Model/XButtonDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XBUTTONDELEGATE_HPP 2 | #define XBUTTONDELEGATE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "XPersistentItemDelegate.hpp" 11 | 12 | namespace FinTechUI 13 | { 14 | 15 | class XButtonDelegate : public XPersistentItemDelegate 16 | { 17 | Q_OBJECT 18 | public: 19 | explicit XButtonDelegate(QString text, QObject *parent = NULL): XPersistentItemDelegate(parent) 20 | { 21 | m_Text = text; 22 | } 23 | 24 | virtual ~XButtonDelegate() 25 | { 26 | 27 | } 28 | public: 29 | // 因为按钮既不需要从model读取数据,也不需要写入,因此仅需要重写一个函数即可 30 | virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const 31 | { 32 | QPersistentModelIndex perIndex(index); 33 | QWidget *widget = new QWidget(parent); 34 | widget->setAutoFillBackground(true); 35 | QHBoxLayout *layout = new QHBoxLayout; 36 | layout->setContentsMargins(0, 0, 0, 0); 37 | layout->setMargin(0); 38 | layout->addStretch(); 39 | QPushButton* button = new QPushButton(m_Text); 40 | button->setMinimumHeight(25); 41 | layout->addWidget(button); 42 | layout->addStretch(); 43 | widget->setLayout(layout); 44 | QObject::connect(button, &QPushButton::clicked, [=]{ 45 | QModelIndex Index = static_cast(perIndex); 46 | //const成员里,不能修改对象,因此不能emit信号 47 | auto temp = const_cast(this); 48 | emit temp->Clicked(Index); 49 | }); 50 | 51 | return widget; 52 | } 53 | 54 | signals: 55 | void Clicked(const QModelIndex index); 56 | protected: 57 | QString m_Text; 58 | }; 59 | 60 | } 61 | 62 | #endif // XBUTTONDELEGATE_HPP 63 | -------------------------------------------------------------------------------- /Model/XComboBoxDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XCOMBOBOXDELEGATE_HPP 2 | #define XCOMBOBOXDELEGATE_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace FinTechUI 8 | { 9 | class XComboBoxDelegate : public QStyledItemDelegate 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit XComboBoxDelegate(QObject* parent = NULL) : QStyledItemDelegate(parent) 14 | { 15 | 16 | } 17 | virtual ~XComboBoxDelegate() 18 | { 19 | 20 | } 21 | public: 22 | virtual QWidget *createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const 23 | { 24 | Q_UNUSED(option) 25 | QStringList items = index.model()->data(index, Qt::UserRole).toStringList(); 26 | QComboBox* editor = new QComboBox(parent); 27 | editor->addItems(items); 28 | return editor; 29 | } 30 | 31 | virtual void setEditorData(QWidget *editor, const QModelIndex &index) const 32 | { 33 | QString text = index.model()->data(index, Qt::DisplayRole).toString(); 34 | QStringList items = index.model()->data(index, Qt::UserRole).toStringList(); 35 | QComboBox* comboBox = static_cast (editor); 36 | if(comboBox != NULL) 37 | { 38 | comboBox->setCurrentText(text); 39 | } 40 | } 41 | 42 | virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 43 | { 44 | QComboBox *comboBox = static_cast(editor); 45 | if(comboBox != NULL) 46 | { 47 | model->setData(index, comboBox->currentText(), Qt::DisplayRole); 48 | } 49 | } 50 | 51 | virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const 52 | { 53 | editor->setGeometry(option.rect); 54 | } 55 | }; 56 | 57 | } 58 | 59 | #endif // XCOMBOBOXDELEGATE_HPP -------------------------------------------------------------------------------- /Model/XHeaderView.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XHEADERVIEW_HPP 2 | #define XHEADERVIEW_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace FinTechUI{ 15 | 16 | #define NAME "name" 17 | #define ITEMS "items" 18 | 19 | enum EHeaderRole 20 | { 21 | COLUMN_SPAN_ROLE = Qt::UserRole + 1, 22 | ROW_SPAN_ROLE, 23 | COLUMN_SIZE_ROLE, 24 | ROW_SIZE_ROLE, 25 | }; 26 | 27 | class XHeaderItem 28 | { 29 | public: 30 | XHeaderItem(XHeaderItem* parent = 0) : m_ParentItem(parent) 31 | { 32 | m_Row = 0; 33 | m_Column = 0; 34 | } 35 | 36 | XHeaderItem(int row, int column, XHeaderItem* parent = NULL) : m_ParentItem(parent) 37 | { 38 | m_Row = row; 39 | m_Column = column; 40 | } 41 | 42 | ~XHeaderItem() 43 | { 44 | 45 | } 46 | 47 | XHeaderItem* insertChild(int row, int col) 48 | { 49 | XHeaderItem* newChild = new XHeaderItem(row, col, this); 50 | m_ChildItems.insert(QPair(row, col), newChild); 51 | return newChild; 52 | } 53 | 54 | const XHeaderItem* child(int row, int col) const 55 | { 56 | QHash, XHeaderItem*>::const_iterator itr = m_ChildItems.find(QPair(row, col)); 57 | if (itr != m_ChildItems.end()) 58 | return itr.value(); 59 | return NULL; 60 | } 61 | 62 | XHeaderItem* child(int row, int col) 63 | { 64 | QHash, XHeaderItem*>::iterator itr = m_ChildItems.find(QPair(row, col)); 65 | if (itr != m_ChildItems.end()) 66 | return itr.value(); 67 | return NULL; 68 | } 69 | 70 | void setData(const QVariant& data, int role) 71 | { 72 | m_RoleDatas.insert(role, data); 73 | } 74 | 75 | QVariant data(int role = Qt::UserRole+1) const 76 | { 77 | QHash::const_iterator itr = m_RoleDatas.find(role); 78 | if (itr != m_RoleDatas.end()) 79 | return itr.value(); 80 | return QVariant(); 81 | } 82 | 83 | inline int column() const 84 | { 85 | return m_Column; 86 | } 87 | 88 | inline int row() const 89 | { 90 | return m_Row; 91 | } 92 | 93 | XHeaderItem* parent() 94 | { 95 | return m_ParentItem; 96 | } 97 | 98 | void setText(const QString& text) 99 | { 100 | m_RoleDatas.insert(Qt::DisplayRole, text); 101 | } 102 | 103 | void clear() 104 | { 105 | QList items = m_ChildItems.values(); 106 | foreach(XHeaderItem* item, m_ChildItems) 107 | { 108 | if(item) 109 | { 110 | delete item; 111 | } 112 | } 113 | m_ChildItems.clear(); 114 | } 115 | 116 | private: 117 | int m_Row; 118 | int m_Column; 119 | XHeaderItem* m_ParentItem; 120 | QHash, XHeaderItem*> m_ChildItems; 121 | QHash m_RoleDatas; 122 | }; 123 | 124 | class XHeaderModel : public QAbstractTableModel 125 | { 126 | Q_OBJECT 127 | public: 128 | XHeaderModel(int rows, int cols, QObject* parent = NULL) : QAbstractTableModel(parent) 129 | { 130 | m_RowCount = rows; 131 | m_ColumnCount = cols; 132 | m_RootItem = new XHeaderItem(); 133 | } 134 | 135 | virtual ~XHeaderModel() 136 | { 137 | m_RootItem->clear(); 138 | delete m_RootItem; 139 | } 140 | 141 | public: 142 | virtual QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const 143 | { 144 | if (!hasIndex(row,column,parent)) 145 | return QModelIndex(); 146 | 147 | XHeaderItem* parentItem; 148 | if (!parent.isValid()) 149 | parentItem = m_RootItem; // parent item is always the root_item on table model 150 | else 151 | parentItem = static_cast(parent.internalPointer()); // no effect 152 | 153 | XHeaderItem* childItem = parentItem->child(row, column); 154 | if (!childItem) 155 | childItem = parentItem->insertChild(row,column); 156 | return createIndex(row,column,childItem); 157 | return QModelIndex(); 158 | } 159 | 160 | virtual int rowCount(const QModelIndex& parent = QModelIndex()) const 161 | { 162 | Q_UNUSED(parent); 163 | return m_RowCount; 164 | } 165 | 166 | virtual int columnCount(const QModelIndex& parent = QModelIndex()) const 167 | { 168 | Q_UNUSED(parent); 169 | return m_ColumnCount; 170 | } 171 | 172 | virtual QVariant data(const QModelIndex &index, int role) const 173 | { 174 | if (!index.isValid()) 175 | return QVariant(); 176 | 177 | if (index.row() >= m_RowCount || index.row() < 0 || index.column() >= m_ColumnCount || index.column() < 0) 178 | return QVariant(); 179 | 180 | XHeaderItem* item = static_cast(index.internalPointer()); 181 | 182 | return item->data(role); 183 | } 184 | 185 | virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) 186 | { 187 | if (index.isValid()) 188 | { 189 | XHeaderItem* item = static_cast(index.internalPointer()); 190 | if (role == COLUMN_SPAN_ROLE) 191 | { 192 | int col = index.column(); 193 | int span = value.toInt(); 194 | if (span > 0) // span size should be more than 1, else nothing to do 195 | { 196 | if (col + span - 1 >= m_ColumnCount) // span size should be less than whole columns, 197 | span = m_ColumnCount - col; 198 | item->setData(span, COLUMN_SPAN_ROLE); 199 | } 200 | } 201 | else if (role == ROW_SPAN_ROLE) 202 | { 203 | int row = index.row(); 204 | int span = value.toInt(); 205 | if (span > 0) // span size should be more than 1, else nothing to do 206 | { 207 | if (row + span - 1 >= m_RowCount) 208 | span = m_RowCount - row; 209 | item->setData(span, ROW_SPAN_ROLE); 210 | } 211 | } 212 | else 213 | { 214 | item->setData(value, role); 215 | } 216 | return true; 217 | } 218 | return false; 219 | } 220 | 221 | virtual Qt::ItemFlags flags(const QModelIndex &index) const 222 | { 223 | if (!index.isValid()) 224 | return Qt::ItemIsEnabled; 225 | return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; 226 | } 227 | private: 228 | int m_RowCount; 229 | int m_ColumnCount; 230 | XHeaderItem* m_RootItem; 231 | }; 232 | 233 | class XHeaderView : public QHeaderView 234 | { 235 | Q_OBJECT 236 | public: 237 | explicit XHeaderView(const QList& headerData, Qt::Orientation orientation = Qt::Horizontal, QWidget* parent = 0) : QHeaderView(orientation, parent) 238 | { 239 | int rows = 2; 240 | QSize baseSectionSize; 241 | if (orientation == Qt::Horizontal) 242 | { 243 | baseSectionSize.setWidth(defaultSectionSize()); 244 | baseSectionSize.setHeight(25); 245 | } 246 | else 247 | { 248 | baseSectionSize.setWidth(50); 249 | baseSectionSize.setHeight(defaultSectionSize()); 250 | } 251 | int columns = 0; 252 | foreach(QVariantMap data, headerData) 253 | { 254 | columns += data[ITEMS].toStringList().size(); 255 | } 256 | 257 | // create header model 258 | XHeaderModel* headerModel = new XHeaderModel(rows, columns); 259 | 260 | // set default size of item 261 | for (int row = 0; row < rows; ++row) 262 | { 263 | for (int col = 0; col < columns; ++col) 264 | { 265 | headerModel->setData(headerModel->index(row, col), baseSectionSize, Qt::SizeHintRole); 266 | } 267 | } 268 | setModel(headerModel); 269 | // set Header Model Data 270 | columns = 0; 271 | for (int i = 0; i < headerData.size(); i++) 272 | { 273 | QVariantMap data = headerData.at(i); 274 | QString name = data[NAME].toString(); 275 | QStringList items = data[ITEMS].toStringList(); 276 | // firt row 277 | if(items.size() > 1) 278 | { 279 | setSpan(0, columns, 0, items.size()); 280 | } 281 | else 282 | { 283 | setSpan(0, columns, 2, 0); 284 | } 285 | headerModel->setData(headerModel->index(0, columns), name, Qt::DisplayRole); 286 | // second row 287 | for(int j = 0; j < items.size(); j++) 288 | { 289 | headerModel->setData(headerModel->index(1, columns + j), items.at(j), Qt::DisplayRole); 290 | } 291 | columns += items.size(); 292 | } 293 | connect(this, SIGNAL(sectionResized(int, int, int)), this, SLOT(onSectionResized(int, int, int))); 294 | } 295 | 296 | explicit XHeaderView(Qt::Orientation orientation, int rows, int columns, QWidget* parent = 0) : QHeaderView(orientation, parent) 297 | { 298 | QSize baseSectionSize; 299 | if (orientation == Qt::Horizontal) 300 | { 301 | baseSectionSize.setWidth(defaultSectionSize()); 302 | baseSectionSize.setHeight(20); 303 | } 304 | else 305 | { 306 | baseSectionSize.setWidth(50); 307 | baseSectionSize.setHeight(defaultSectionSize()); 308 | } 309 | 310 | // create header model 311 | XHeaderModel* headerModel = new XHeaderModel(rows,columns); 312 | 313 | // set default size of item 314 | for (int row = 0; row < rows; ++row) 315 | { 316 | for (int col = 0; col < columns; ++col) 317 | { 318 | headerModel->setData(headerModel->index(row, col), baseSectionSize, Qt::SizeHintRole); 319 | } 320 | } 321 | setModel(headerModel); 322 | 323 | connect(this, SIGNAL(sectionResized(int, int, int)), this, SLOT(onSectionResized(int, int, int))); 324 | } 325 | 326 | virtual ~XHeaderView() 327 | { 328 | 329 | } 330 | 331 | void setRowHeight(int row, int rowHeight) 332 | { 333 | XHeaderModel* md = qobject_cast(model()); 334 | const int cols = md->columnCount(); 335 | for (int col = 0; col < cols; ++col) 336 | { 337 | QSize sz = md->index(row, col).data(Qt::SizeHintRole).toSize(); 338 | sz.setHeight(rowHeight); 339 | md->setData(md->index(row, col), sz, Qt::SizeHintRole); 340 | } 341 | if (orientation() == Qt::Vertical) 342 | resizeSection(row, rowHeight); 343 | } 344 | 345 | void setColumnWidth(int col, int colWidth) 346 | { 347 | XHeaderModel* md = qobject_cast(model()); 348 | const int rows = md->rowCount(); 349 | for (int row = 0; row < rows; ++row) 350 | { 351 | QSize sz = md->index(row, col).data(Qt::SizeHintRole).toSize(); 352 | sz.setWidth(colWidth); 353 | md->setData(md->index(row, col), sz, Qt::SizeHintRole); 354 | } 355 | if (orientation() == Qt::Horizontal) 356 | resizeSection(col, colWidth); 357 | } 358 | 359 | void setSpan(int row, int column, int rowSpanCount, int columnSpanCount) 360 | { 361 | XHeaderModel* md = qobject_cast(model()); 362 | QModelIndex idx = md->index(row, column); 363 | if (rowSpanCount > 0) 364 | md->setData(idx, rowSpanCount, ROW_SPAN_ROLE); 365 | if(columnSpanCount) 366 | { 367 | md->setData(idx, columnSpanCount, COLUMN_SPAN_ROLE); 368 | } 369 | } 370 | 371 | void setCellBackgroundColor(const QModelIndex& index, const QColor& color) 372 | { 373 | XHeaderModel* md = qobject_cast(model()); 374 | md->setData(index, color, Qt::BackgroundRole); 375 | } 376 | 377 | void setCellForegroundColor(const QModelIndex& index, const QColor& color) 378 | { 379 | XHeaderModel* md = qobject_cast(model()); 380 | md->setData(index, color, Qt::ForegroundRole); 381 | } 382 | 383 | protected: 384 | virtual void mousePressEvent(QMouseEvent* event) 385 | { 386 | QHeaderView::mousePressEvent(event); 387 | QPoint pos = event->pos(); 388 | QModelIndex index = indexAt(pos); 389 | const int OTN = orientation(); 390 | if (index.isValid()) 391 | { 392 | int beginSection = -1; 393 | int endSection = -1; 394 | int numbers = 0; 395 | numbers = getSectionRange(index, &beginSection, &endSection); 396 | if(numbers > 0) 397 | { 398 | emit sectionPressed(beginSection,endSection); 399 | return; 400 | } 401 | else 402 | { 403 | const XHeaderModel* tblModel = qobject_cast(this->model()); 404 | const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount(); 405 | int logicalIdx = (OTN == Qt::Horizontal) ? index.column() : index.row(); 406 | int curLevel = (OTN == Qt::Horizontal) ? index.row() : index.column(); 407 | for (int i = 0; i < LEVEL_CNT; ++i) 408 | { 409 | QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIdx) : tblModel->index(logicalIdx, i); 410 | numbers = getSectionRange(cellIndex,&beginSection,&endSection); 411 | if (numbers > 0) 412 | { 413 | if (beginSection <= logicalIdx && logicalIdx <= endSection) 414 | { 415 | int beginLevel = (OTN == Qt::Horizontal) ? cellIndex.row() : cellIndex.column(); 416 | QVariant levelSpanCnt = cellIndex.data((OTN == Qt::Horizontal)?ROW_SPAN_ROLE:COLUMN_SPAN_ROLE); 417 | if (!levelSpanCnt.isValid()) 418 | continue; 419 | int endLevel = beginLevel + levelSpanCnt.toInt() - 1; 420 | if (beginLevel <= curLevel && curLevel <= endLevel) 421 | { 422 | emit sectionPressed(beginSection,endSection); 423 | break; 424 | } 425 | } 426 | } 427 | } 428 | } 429 | } 430 | } 431 | 432 | virtual QModelIndex indexAt(const QPoint& pos) 433 | { 434 | const XHeaderModel* tblModel = qobject_cast(this->model()); 435 | const int OTN = orientation(); 436 | const int ROWS = tblModel->rowCount(); 437 | const int COLS = tblModel->columnCount(); 438 | int logicalIdx = logicalIndexAt(pos); 439 | 440 | if (OTN == Qt::Horizontal) 441 | { 442 | int dY=0; 443 | for (int row = 0; row < ROWS; ++row) 444 | { 445 | QModelIndex cellIndex = tblModel->index(row, logicalIdx); 446 | dY += cellIndex.data(Qt::SizeHintRole).toSize().height(); 447 | if (pos.y() <= dY) 448 | return cellIndex; 449 | } 450 | } 451 | else 452 | { 453 | int dX=0; 454 | for (int col = 0; col < COLS; ++col) 455 | { 456 | QModelIndex cellIndex = tblModel->index(logicalIdx,col); 457 | dX += cellIndex.data(Qt::SizeHintRole).toSize().width(); 458 | if (pos.x() <= dX) 459 | return cellIndex; 460 | } 461 | } 462 | 463 | return QModelIndex(); 464 | } 465 | 466 | virtual void paintSection(QPainter* painter, const QRect& rect, int logicalIdx) const 467 | { 468 | const XHeaderModel* tblModel = qobject_cast(this->model()); 469 | const int OTN = orientation(); 470 | const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount(); 471 | for (int i = 0; i < LEVEL_CNT; ++i) 472 | { 473 | QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIdx) : tblModel->index(logicalIdx, i); 474 | QSize cellSize = cellIndex.data(Qt::SizeHintRole).toSize(); 475 | QRect sectionRect(rect); 476 | 477 | // set position of the cell 478 | if (OTN == Qt::Horizontal) 479 | sectionRect.setTop(rowSpanSize(logicalIdx, 0, i)); // distance from 0 to i-1 rows 480 | else 481 | sectionRect.setLeft(columnSpanSize(logicalIdx, 0, i)); 482 | 483 | sectionRect.setSize(cellSize); 484 | 485 | // check up span column or row 486 | QModelIndex colSpanIdx = columnSpanIndex(cellIndex); 487 | QModelIndex rowSpanIdx = rowSpanIndex(cellIndex); 488 | if (colSpanIdx.isValid()) 489 | { 490 | int colSpanFrom = colSpanIdx.column(); 491 | int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt(); 492 | int colSpanTo = colSpanFrom + colSpanCnt - 1; 493 | int colSpan = columnSpanSize(cellIndex.row(), colSpanFrom, colSpanCnt); 494 | if (OTN == Qt::Horizontal) 495 | sectionRect.setLeft(sectionViewportPosition(colSpanFrom)); 496 | else 497 | { 498 | sectionRect.setLeft(columnSpanSize(logicalIdx, 0, colSpanFrom)); 499 | i = colSpanTo; 500 | } 501 | 502 | sectionRect.setWidth(colSpan); 503 | 504 | // check up if the column span index has row span 505 | QVariant subRowSpanData = colSpanIdx.data(ROW_SPAN_ROLE); 506 | if (subRowSpanData.isValid()) 507 | { 508 | int subRowSpanFrom = colSpanIdx.row(); 509 | int subRowSpanCnt = subRowSpanData.toInt(); 510 | int subRowSpanTo = subRowSpanFrom+subRowSpanCnt - 1; 511 | int subRowSpan = rowSpanSize(colSpanFrom, subRowSpanFrom, subRowSpanCnt); 512 | if (OTN == Qt::Vertical) 513 | sectionRect.setTop(sectionViewportPosition(subRowSpanFrom)); 514 | else 515 | { 516 | sectionRect.setTop(rowSpanSize(colSpanFrom,0,subRowSpanFrom)); 517 | i = subRowSpanTo; 518 | } 519 | sectionRect.setHeight(subRowSpan); 520 | } 521 | cellIndex=colSpanIdx; 522 | } 523 | if (rowSpanIdx.isValid()) 524 | { 525 | int rowSpanFrom = rowSpanIdx.row(); 526 | int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt(); 527 | int rowSpanTo = rowSpanFrom + rowSpanCnt - 1; 528 | int rowSpan = rowSpanSize(cellIndex.column(), rowSpanFrom, rowSpanCnt); 529 | if (OTN == Qt::Vertical) 530 | sectionRect.setTop(sectionViewportPosition(rowSpanFrom)); 531 | else 532 | { 533 | sectionRect.setTop(rowSpanSize(logicalIdx, 0, rowSpanFrom)); 534 | i = rowSpanTo; 535 | } 536 | sectionRect.setHeight(rowSpan); 537 | 538 | // check up if the row span index has column span 539 | QVariant subColSpanData = rowSpanIdx.data(COLUMN_SPAN_ROLE); 540 | if (subColSpanData.isValid()) 541 | { 542 | int subColSpanFrom = rowSpanIdx.column(); 543 | int subColSpanCnt = subColSpanData.toInt(); 544 | int subColSpanTo = subColSpanFrom + subColSpanCnt - 1; 545 | int subColSpan = columnSpanSize(rowSpanFrom, subColSpanFrom, subColSpanCnt); 546 | if (OTN == Qt::Horizontal) 547 | sectionRect.setLeft(sectionViewportPosition(subColSpanFrom)); 548 | else 549 | { 550 | sectionRect.setLeft(columnSpanSize(rowSpanFrom, 0, subColSpanFrom)); 551 | i = subColSpanTo; 552 | } 553 | sectionRect.setWidth(subColSpan); 554 | } 555 | cellIndex=rowSpanIdx; 556 | } 557 | 558 | // draw section with style 559 | QStyleOptionHeader sectionStyle; 560 | initStyleOption(§ionStyle); 561 | sectionStyle.textAlignment = Qt::AlignCenter; 562 | sectionStyle.iconAlignment = Qt::AlignVCenter; 563 | sectionStyle.section = logicalIdx; 564 | sectionStyle.text = cellIndex.data(Qt::DisplayRole).toString(); 565 | sectionStyle.rect = sectionRect; 566 | 567 | // file background or foreground color of the cell 568 | QVariant bg = cellIndex.data(Qt::BackgroundRole); 569 | QVariant fg = cellIndex.data(Qt::ForegroundRole); 570 | if (bg.canConvert(QVariant::Brush)) 571 | { 572 | sectionStyle.palette.setBrush(QPalette::Button, qvariant_cast(bg)); 573 | sectionStyle.palette.setBrush(QPalette::Window, qvariant_cast(bg)); 574 | } 575 | if (fg.canConvert(QVariant::Brush)) 576 | { 577 | sectionStyle.palette.setBrush(QPalette::ButtonText, qvariant_cast(fg)); 578 | } 579 | 580 | painter->save(); 581 | qDrawShadePanel(painter, sectionStyle.rect, sectionStyle.palette, false, 1, §ionStyle.palette.brush(QPalette::Button)); 582 | style()->drawControl(QStyle::CE_HeaderLabel, §ionStyle, painter); 583 | painter->restore(); 584 | } 585 | } 586 | 587 | virtual QSize sectionSizeFromContents(int logicalIndex) const 588 | { 589 | const XHeaderModel* tblModel = qobject_cast(this->model()); 590 | const int OTN = orientation(); 591 | const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount():tblModel->columnCount(); 592 | 593 | QSize siz = QHeaderView::sectionSizeFromContents(logicalIndex); 594 | for (int i = 0; i < LEVEL_CNT; ++i) 595 | { 596 | QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIndex) : tblModel->index(logicalIndex, i); 597 | QModelIndex colSpanIdx = columnSpanIndex(cellIndex); 598 | QModelIndex rowSpanIdx = rowSpanIndex(cellIndex); 599 | siz = cellIndex.data(Qt::SizeHintRole).toSize(); 600 | 601 | if (colSpanIdx.isValid()) 602 | { 603 | int colSpanFrom = colSpanIdx.column(); 604 | int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt(); 605 | int colSpanTo = colSpanFrom + colSpanCnt - 1; 606 | siz.setWidth(columnSpanSize(colSpanIdx.row(), colSpanFrom, colSpanCnt)); 607 | if (OTN == Qt::Vertical) 608 | i = colSpanTo; 609 | } 610 | if (rowSpanIdx.isValid()) 611 | { 612 | int rowSpanFrom = rowSpanIdx.row(); 613 | int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt(); 614 | int rowSpanTo = rowSpanFrom + rowSpanCnt - 1; 615 | siz.setHeight(rowSpanSize(rowSpanIdx.column(), rowSpanFrom, rowSpanCnt)); 616 | if (OTN == Qt::Horizontal) 617 | i = rowSpanTo; 618 | } 619 | } 620 | return siz; 621 | } 622 | 623 | protected: 624 | QModelIndex columnSpanIndex(const QModelIndex& currentIndex) const 625 | { 626 | const XHeaderModel* tblModel = qobject_cast(model()); 627 | const int curRow = currentIndex.row(); 628 | const int curCol = currentIndex.column(); 629 | int i = curCol; 630 | while (i >= 0) 631 | { 632 | QModelIndex spanIndex = tblModel->index(curRow,i); 633 | QVariant span = spanIndex.data(COLUMN_SPAN_ROLE); 634 | if (span.isValid() && spanIndex.column() + span.toInt() - 1 >= curCol) 635 | return spanIndex; 636 | i--; 637 | } 638 | return QModelIndex(); 639 | } 640 | 641 | QModelIndex rowSpanIndex(const QModelIndex& currentIdx) const 642 | { 643 | const XHeaderModel* tblModel = qobject_cast(model()); 644 | const int curRow = currentIdx.row(); 645 | const int curCol = currentIdx.column(); 646 | int i = curRow; 647 | while (i >= 0) 648 | { 649 | QModelIndex spanIndex = tblModel->index(i,curCol); 650 | QVariant span = spanIndex.data(ROW_SPAN_ROLE); 651 | if (span.isValid() && spanIndex.row() + span.toInt() - 1 >= curRow) 652 | return spanIndex; 653 | i--; 654 | } 655 | return QModelIndex(); 656 | } 657 | 658 | int columnSpanSize(int row, int from, int spanCount) const 659 | { 660 | const XHeaderModel* tblModel = qobject_cast(model()); 661 | int span = 0; 662 | for (int i = from; i < from + spanCount; ++i) 663 | { 664 | QSize cellSize = tblModel->index(row,i).data(Qt::SizeHintRole).toSize(); 665 | span += cellSize.width(); 666 | } 667 | return span; 668 | } 669 | 670 | int rowSpanSize(int column, int from, int spanCount) const 671 | { 672 | const XHeaderModel* tblModel = qobject_cast(model()); 673 | int span = 0; 674 | for (int i = from; i < from+spanCount; ++i) 675 | { 676 | QSize cellSize = tblModel->index(i, column).data(Qt::SizeHintRole).toSize(); 677 | span += cellSize.height(); 678 | } 679 | return span; 680 | } 681 | 682 | int getSectionRange(QModelIndex&index, int* beginSection, int* endSection) const 683 | { 684 | // check up section range from the index 685 | QModelIndex colSpanIdx = columnSpanIndex(index); 686 | QModelIndex rowSpanIdx = rowSpanIndex(index); 687 | 688 | if (colSpanIdx.isValid()) 689 | { 690 | int colSpanFrom = colSpanIdx.column(); 691 | int colSpanCnt = colSpanIdx.data(COLUMN_SPAN_ROLE).toInt(); 692 | int colSpanTo = colSpanFrom + colSpanCnt - 1; 693 | if (orientation() == Qt::Horizontal) 694 | { 695 | *beginSection = colSpanFrom; 696 | *endSection = colSpanTo; 697 | index = colSpanIdx; 698 | return colSpanCnt; 699 | } 700 | else 701 | { 702 | // check up if the column span index has row span 703 | QVariant subRowSpanData = colSpanIdx.data(ROW_SPAN_ROLE); 704 | if (subRowSpanData.isValid()) 705 | { 706 | int subRowSpanFrom = colSpanIdx.row(); 707 | int subRowSpanCnt = subRowSpanData.toInt(); 708 | int subRowSpanTo = subRowSpanFrom+subRowSpanCnt - 1; 709 | *beginSection = subRowSpanFrom; 710 | *endSection = subRowSpanTo; 711 | index = colSpanIdx; 712 | return subRowSpanCnt; 713 | } 714 | } 715 | } 716 | 717 | if (rowSpanIdx.isValid()) 718 | { 719 | int rowSpanFrom = rowSpanIdx.row(); 720 | int rowSpanCnt = rowSpanIdx.data(ROW_SPAN_ROLE).toInt(); 721 | int rowSpanTo = rowSpanFrom + rowSpanCnt - 1; 722 | if (orientation() == Qt::Vertical) 723 | { 724 | *beginSection = rowSpanFrom; 725 | *endSection = rowSpanTo; 726 | index = rowSpanIdx; 727 | return rowSpanCnt; 728 | } 729 | else 730 | { 731 | // check up if the row span index has column span 732 | QVariant subColSpanData = rowSpanIdx.data(COLUMN_SPAN_ROLE); 733 | if (subColSpanData.isValid()) 734 | { 735 | int subColSpanFrom = rowSpanIdx.column(); 736 | int subColSpanCnt = subColSpanData.toInt(); 737 | int subColSpanTo = subColSpanFrom + subColSpanCnt - 1; 738 | *beginSection = subColSpanFrom; 739 | *endSection = subColSpanTo; 740 | index = rowSpanIdx; 741 | return subColSpanCnt; 742 | } 743 | } 744 | } 745 | 746 | return 0; 747 | } 748 | 749 | protected slots: 750 | void onSectionResized(int logicalIndex, int oldSize, int newSize) 751 | { 752 | XHeaderModel* tblModel = qobject_cast(this->model()); 753 | const int OTN = orientation(); 754 | const int LEVEL_CNT = (OTN == Qt::Horizontal) ? tblModel->rowCount() : tblModel->columnCount(); 755 | int pos = sectionViewportPosition(logicalIndex); 756 | int xx = (OTN == Qt::Horizontal) ? pos : 0; 757 | int yy = (OTN == Qt::Horizontal) ? 0 : pos; 758 | QRect sectionRect(xx, yy, 0, 0); 759 | for (int i = 0; i< LEVEL_CNT; ++i) 760 | { 761 | QModelIndex cellIndex = (OTN == Qt::Horizontal) ? tblModel->index(i, logicalIndex) :tblModel->index(logicalIndex, i); 762 | QSize cellSize = cellIndex.data(Qt::SizeHintRole).toSize(); 763 | // set position of cell 764 | if (OTN == Qt::Horizontal) 765 | { 766 | sectionRect.setTop(rowSpanSize(logicalIndex, 0, i)); 767 | cellSize.setWidth(newSize); 768 | } 769 | else 770 | { 771 | sectionRect.setLeft(columnSpanSize(logicalIndex, 0, i)); 772 | cellSize.setHeight(newSize); 773 | } 774 | tblModel->setData(cellIndex, cellSize, Qt::SizeHintRole); 775 | 776 | QModelIndex colSpanIdx = columnSpanIndex(cellIndex); 777 | QModelIndex rowSpanIdx = rowSpanIndex(cellIndex); 778 | 779 | if (colSpanIdx.isValid()) 780 | { 781 | int colSpanFrom = colSpanIdx.column(); 782 | if (OTN == Qt::Horizontal) 783 | sectionRect.setLeft(sectionViewportPosition(colSpanFrom)); 784 | else 785 | { 786 | sectionRect.setLeft(columnSpanSize(logicalIndex, 0, colSpanFrom)); 787 | } 788 | 789 | } 790 | if (rowSpanIdx.isValid()) 791 | { 792 | int rowSpanFrom = rowSpanIdx.row(); 793 | if (OTN == Qt::Vertical) 794 | sectionRect.setTop(sectionViewportPosition(rowSpanFrom)); 795 | else 796 | sectionRect.setTop(rowSpanSize(logicalIndex, 0, rowSpanFrom)); 797 | } 798 | QRect rToUpdate(sectionRect); 799 | rToUpdate.setWidth(viewport()->width() - sectionRect.left()); 800 | rToUpdate.setHeight(viewport()->height() - sectionRect.top()); 801 | viewport()->update(rToUpdate.normalized()); 802 | } 803 | } 804 | 805 | signals: 806 | void sectionPressed(int from, int to); 807 | }; 808 | } 809 | 810 | #endif // XHEADERVIEW_HPP 811 | -------------------------------------------------------------------------------- /Model/XHeaderViewTest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XHEADERVIEWTEST_HPP 2 | #define XHEADERVIEWTEST_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "XHeaderView.hpp" 8 | 9 | namespace FinTechUI{ 10 | 11 | class MainWindow : public QWidget 12 | { 13 | Q_OBJECT 14 | public: 15 | MainWindow(QWidget* parent = 0) : QWidget(parent) 16 | { 17 | QStandardItemModel* dataModel = new QStandardItemModel; 18 | QList headerData; 19 | { 20 | QVariantMap data; 21 | data["name"] = "Colo"; 22 | QStringList items; 23 | items.append("Colo"); 24 | data["items"] = items; 25 | headerData.append(data); 26 | } 27 | { 28 | QVariantMap data; 29 | data["name"] = "OSVersion"; 30 | QStringList items; 31 | items.append("OSVersion"); 32 | data["items"] = items; 33 | headerData.append(data); 34 | } 35 | { 36 | QVariantMap data; 37 | data["name"] = "LoadAverage"; 38 | QStringList items; 39 | items.append("min1"); 40 | items.append("min5"); 41 | items.append("min15"); 42 | items.append("CPUS"); 43 | data["items"] = items; 44 | headerData.append(data); 45 | } 46 | 47 | { 48 | QVariantMap data; 49 | data["name"] = "CPUUsage"; 50 | QStringList items; 51 | items.append("UserRate"); 52 | items.append("SysRate"); 53 | items.append("IdleRate"); 54 | items.append("IOWaitRate"); 55 | items.append("IrqRate"); 56 | items.append("SoftIrqRate"); 57 | items.append("UsedRate"); 58 | data["items"] = items; 59 | headerData.append(data); 60 | } 61 | 62 | { 63 | QVariantMap data; 64 | data["name"] = "MemoryInfo"; 65 | QStringList items; 66 | items.append("Total"); 67 | items.append("Free"); 68 | items.append("UsedRate"); 69 | data["items"] = items; 70 | headerData.append(data); 71 | } 72 | 73 | { 74 | QVariantMap data; 75 | data["name"] = "DiskInfo"; 76 | QStringList items; 77 | items.append("Total"); 78 | items.append("Free"); 79 | items.append("UsedRate"); 80 | items.append("Mount1UsedRate"); 81 | items.append("Mount2UsedRate"); 82 | data["items"] = items; 83 | headerData.append(data); 84 | } 85 | 86 | XHeaderView* hHead = new XHeaderView(headerData); 87 | 88 | // XHeaderView* hHead = new XHeaderView(Qt::Horizontal, 3, 4); 89 | // QAbstractItemModel* hModel = hHead->model(); 90 | // hHead->setSpan(0,0,3,0); 91 | // hHead->setSpan(0,1,2,2); 92 | // hHead->setSpan(1,3,2,0); 93 | // hModel->setData(hModel->index(0,0),QString("cell1"),Qt::DisplayRole); 94 | // hModel->setData(hModel->index(0,1),QString("cell2"),Qt::DisplayRole); 95 | // hModel->setData(hModel->index(2,1),QString("cell3"),Qt::DisplayRole); 96 | // hModel->setData(hModel->index(2,2),QString("cell4"),Qt::DisplayRole); 97 | // hModel->setData(hModel->index(0,3),QString("cell5"),Qt::DisplayRole); 98 | // hModel->setData(hModel->index(1,3),QString("cell6"),Qt::DisplayRole); 99 | 100 | XHeaderView* vHead = new XHeaderView(Qt::Vertical,4,3); 101 | QAbstractItemModel* vModel = vHead->model(); 102 | vHead->setSpan(0,0,0,3); 103 | vHead->setSpan(1,0,3,0); 104 | vHead->setSpan(1,1,2,0); 105 | vModel->setData(vModel->index(0,0),QString("cell1"),Qt::DisplayRole); 106 | vModel->setData(vModel->index(1,0),QString("cell2"),Qt::DisplayRole); 107 | vModel->setData(vModel->index(1,1),QString("cell3"),Qt::DisplayRole); 108 | vModel->setData(vModel->index(3,1),QString("cell4"),Qt::DisplayRole); 109 | vModel->setData(vModel->index(1,2),QString("cell5"),Qt::DisplayRole); 110 | vModel->setData(vModel->index(2,2),QString("cell6"),Qt::DisplayRole); 111 | vModel->setData(vModel->index(3,2),QString("cell7"),Qt::DisplayRole); 112 | 113 | // hHead->setRowHeight(0,30); 114 | // hHead->setRowHeight(1,30); 115 | // hHead->setRowHeight(2,30); 116 | 117 | vHead->setRowHeight(0,30); 118 | vHead->setRowHeight(1,30); 119 | vHead->setRowHeight(2,30); 120 | vHead->setColumnWidth(0,50); 121 | vHead->setColumnWidth(1,50); 122 | vHead->setColumnWidth(2,50); 123 | 124 | hHead->setSectionsClickable(true); 125 | vHead->setSectionsClickable(true); 126 | 127 | // hHead->setCellBackgroundColor(hModel->index(0,0),0xcfcfcf); 128 | // hHead->setCellBackgroundColor(hModel->index(0,1),0xcfcfcf); 129 | 130 | vHead->setCellBackgroundColor(vModel->index(0,0),Qt::cyan); 131 | vHead->setCellBackgroundColor(vModel->index(1,0),0xcfcfcf); 132 | for (int i=0;i<4;i++) 133 | { 134 | QList items; 135 | for (int j=0;j<21;j++) 136 | { 137 | items.append(new QStandardItem(QString("item(%1,%2)").arg(i).arg(j))); 138 | } 139 | dataModel->appendRow(items); 140 | } 141 | 142 | m_TableView = new QTableView(this); 143 | m_TableView->setModel(dataModel); 144 | m_TableView->setHorizontalHeader(hHead); 145 | m_TableView->setVerticalHeader(vHead); 146 | m_TableView->setSortingEnabled(true); 147 | QVBoxLayout* layout = new QVBoxLayout; 148 | layout->addWidget(m_TableView); 149 | setLayout(layout); 150 | 151 | connect(hHead, SIGNAL(sectionPressed(int, int)), this, SLOT(OnHorizontalHeaderSectionPressed(int,int))); 152 | connect(vHead, SIGNAL(sectionPressed(int, int)), this, SLOT(OnVerticalHeaderSectionPressed(int,int))); 153 | } 154 | 155 | virtual ~MainWindow() 156 | { 157 | 158 | } 159 | protected slots: 160 | void OnHorizontalHeaderSectionPressed(int beginSection, int endSection) 161 | { 162 | m_TableView->clearSelection(); 163 | QAbstractItemView::SelectionMode oldSelectionMode = m_TableView->selectionMode(); 164 | m_TableView->setSelectionMode(QAbstractItemView::MultiSelection); 165 | for (int i=beginSection;iselectColumn(i); 167 | m_TableView->setSelectionMode(oldSelectionMode); 168 | } 169 | 170 | void OnVerticalHeaderSectionPressed(int beginSection, int endSection) 171 | { 172 | m_TableView->clearSelection(); 173 | QAbstractItemView::SelectionMode oldSelectionMode = m_TableView->selectionMode(); 174 | m_TableView->setSelectionMode(QAbstractItemView::MultiSelection); 175 | for (int i=beginSection;iselectRow(i); 177 | m_TableView->setSelectionMode(oldSelectionMode); 178 | } 179 | 180 | private: 181 | QTableView* m_TableView; 182 | }; 183 | 184 | } 185 | 186 | #endif // XHEADERVIEWTEST_HPP -------------------------------------------------------------------------------- /Model/XPersistentItemDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XPERSISTENTITEMDELEGATE_HPP 2 | #define XPERSISTENTITEMDELEGATE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | /* 持久代理类 16 | * 1、View需要将EditTrigger设置为QAbstractItemView::NoEditTriggers,关闭默认的Delegate逻辑 17 | * 2、继承这个类,实现自定义的 持久代理 18 | * 19 | * 继承时,不能也不必重写paint、updateEditorGeometry函数, 20 | * 前者是因为paint本应该做的绘制工作已没有意义,被编辑器覆盖,绘制了也看不到, 21 | * 且在本类中用paint实现了根本逻辑,不能被覆盖。 22 | * 后者是因为本类的paint中会一直重设编辑器的尺寸 23 | * 其他函数可随需要重写,不做要求。 24 | * 25 | * 3、如果需要将数据从编辑器保存到model中,那么需要连接信号槽 到本类的updateModelData,它会调用子类的 26 | * 27 | * void QStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 28 | * 29 | * 因此,此时我们也需要实现这个函数。这也是最重要的额外步骤。 30 | */ 31 | 32 | namespace FinTechUI 33 | { 34 | class XPersistentItemDelegate : public QStyledItemDelegate 35 | { 36 | Q_OBJECT 37 | public: 38 | explicit XPersistentItemDelegate(QObject *parent = NULL) : QStyledItemDelegate(parent) 39 | { 40 | 41 | } 42 | 43 | virtual ~XPersistentItemDelegate() 44 | { 45 | 46 | } 47 | 48 | virtual void paint(QPainter *painter, const QStyleOptionViewItem& option, const QModelIndex& index) const 49 | { 50 | QPersistentModelIndex perIndex(index); 51 | QAbstractItemModel* model = const_cast(index.model()); 52 | //如果model有删除、重置,则删除对应的widget 53 | //如果model有新增,view会调用paint绘制,因此不需做额外工作 54 | connect(model, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(clearWidget()), Qt::UniqueConnection); 55 | connect(model, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(clearWidget()), Qt::UniqueConnection); 56 | connect(model, SIGNAL(destroyed(QObject*)), this, SLOT(clearWidget()), Qt::UniqueConnection); 57 | connect(model, SIGNAL(modelReset()), this, SLOT(clearWidget()), Qt::UniqueConnection); 58 | //如果model有数据变化,更新变化到编辑器 59 | connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector& )), this, SLOT(updateWidget(const QModelIndex&, const QModelIndex&))); 60 | 61 | if(!m_WidgetsMap.contains(perIndex)) 62 | { 63 | QWidget* parentWidget = static_cast(painter->device()); 64 | if(NULL == parentWidget) 65 | return; 66 | QWidget* tempWidget = this->createEditor(parentWidget, option, index); 67 | this->setEditorData(tempWidget, index); 68 | tempWidget->setGeometry(option.rect); 69 | tempWidget->setVisible(true); 70 | m_WidgetsMap.insert(perIndex, tempWidget); 71 | } 72 | else 73 | { 74 | QWidget* tempWidget = m_WidgetsMap.value(perIndex); 75 | if(tempWidget != NULL) 76 | { 77 | tempWidget->setGeometry(option.rect); 78 | } 79 | } 80 | } 81 | 82 | public slots: 83 | void updateModelData() 84 | { 85 | QObject *sender = this->sender(); 86 | if(NULL != sender) 87 | return; 88 | QWidget* editor = static_cast(sender); 89 | if(NULL != editor) 90 | return; 91 | 92 | if(!m_WidgetsMap.values().contains(editor)) 93 | return; 94 | 95 | QPersistentModelIndex perIndex = m_WidgetsMap.key(editor); 96 | if(!perIndex.isValid()) 97 | return; 98 | 99 | QModelIndex index = static_cast(perIndex); 100 | if(!index.isValid()) 101 | return; 102 | 103 | QAbstractItemModel* model = const_cast(index.model()); 104 | this->setModelData(editor,model,index); 105 | 106 | emit model->dataChanged(index,index); 107 | } 108 | private slots: 109 | void clearWidget() 110 | { 111 | auto it = m_WidgetsMap.begin(); 112 | while (it != m_WidgetsMap.end()) 113 | { 114 | if(!it.key().isValid()) 115 | { 116 | it.value()->setParent(NULL); 117 | it.value()->deleteLater(); 118 | it = m_WidgetsMap.erase(it); 119 | } 120 | else 121 | { 122 | ++it; 123 | } 124 | } 125 | } 126 | void updateWidget(const QModelIndex& begin, const QModelIndex& end) 127 | { 128 | QItemSelection selection(begin, end); 129 | QModelIndexList list = selection.indexes(); 130 | 131 | foreach (QModelIndex index, list) 132 | { 133 | QPersistentModelIndex perIndex(index); 134 | if(m_WidgetsMap.contains(perIndex)) 135 | { 136 | QWidget *tempWidget = m_WidgetsMap.value(perIndex); 137 | if(tempWidget) 138 | { 139 | this->setEditorData(tempWidget,index); 140 | } 141 | } 142 | } 143 | } 144 | private: 145 | mutable QMap m_WidgetsMap; 146 | }; 147 | 148 | } 149 | 150 | 151 | #endif // XPERSISTENTITEMDELEGATE_HPP 152 | -------------------------------------------------------------------------------- /Model/XProgressDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XPROGRESSDELEGATE_HPP 2 | #define XPROGRESSDELEGATE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace FinTechUI 9 | { 10 | 11 | class XProgressDelegate : public QStyledItemDelegate 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit XProgressDelegate(QObject* parent = NULL) : QStyledItemDelegate(parent) 16 | { 17 | 18 | } 19 | 20 | virtual ~XProgressDelegate() 21 | { 22 | 23 | } 24 | protected: 25 | virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 26 | { 27 | QStyleOptionViewItem viewOption(option); 28 | initStyleOption(&viewOption, index); 29 | if (option.state.testFlag(QStyle::State_HasFocus)) 30 | viewOption.state = viewOption.state ^ QStyle::State_HasFocus; 31 | 32 | QStyledItemDelegate::paint(painter, viewOption, index); 33 | int nProgress = index.model()->data(index, Qt::DisplayRole).toInt(); 34 | int nLeft = 0; 35 | int nTop = 0; 36 | int nWidth = option.rect.width() - 2 * nLeft; 37 | int nHeight = option.rect.height() - 2 * nTop; 38 | // 设置进度条风格 39 | QStyleOptionProgressBar progressBarOption; 40 | progressBarOption.initFrom(option.widget); 41 | // 进度条显示区域 42 | progressBarOption.rect = QRect(option.rect.left() + nLeft, option.rect.top() + nTop, nWidth, nHeight); 43 | // 设置最小值 44 | progressBarOption.minimum = 0; 45 | // 设置最大值 46 | progressBarOption.maximum = 100; 47 | // 设置对齐方式 48 | progressBarOption.textAlignment = Qt::AlignCenter; 49 | // 设置进度 50 | progressBarOption.progress = nProgress; 51 | // 设置文本(百分比) 52 | progressBarOption.text = QString("%1%").arg(nProgress); 53 | // 设置文本可见 54 | progressBarOption.textVisible = true; 55 | QProgressBar progressBar; 56 | //绘制进度条 57 | QApplication::style()->drawControl(QStyle::CE_ProgressBar, &progressBarOption, painter, &progressBar); 58 | } 59 | 60 | virtual QWidget *createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const 61 | { 62 | return QStyledItemDelegate::createEditor(parent, option, index); 63 | } 64 | 65 | virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const 66 | { 67 | editor->setGeometry(option.rect); 68 | } 69 | 70 | virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const 71 | { 72 | QStyledItemDelegate::setModelData(editor, model, index); 73 | } 74 | 75 | virtual void setEditorData(QWidget *editor, const QModelIndex &index) const 76 | { 77 | QStyledItemDelegate::setEditorData(editor, index); 78 | } 79 | }; 80 | 81 | } 82 | 83 | 84 | #endif // XPROGRESSDELEGATE_HPP -------------------------------------------------------------------------------- /Model/XSortFilterProxyModel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XSORTFILTERPROXYMODEL_H 2 | #define XSORTFILTERPROXYMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace FinTechUI 12 | { 13 | 14 | class XSortFilterProxyModel: public QSortFilterProxyModel 15 | { 16 | Q_OBJECT 17 | public: 18 | explicit XSortFilterProxyModel(): QSortFilterProxyModel() 19 | { 20 | 21 | } 22 | 23 | void setRowFilter(const QMap& filter) 24 | { 25 | m_RowFilter = filter; 26 | } 27 | 28 | void setColumnFilter(const QMap& filter) 29 | { 30 | m_ColumnFilter = filter; 31 | } 32 | 33 | void setColumnType(int column, int type) 34 | { 35 | m_ColumnType[column] = type; 36 | } 37 | 38 | void resetFilter() 39 | { 40 | invalidateFilter(); 41 | } 42 | 43 | protected: 44 | bool lessThan (const QModelIndex& left, const QModelIndex& right) const 45 | { 46 | QVariant LeftItem, RightItem; 47 | LeftItem = left.data(); 48 | RightItem = right.data(); 49 | int Type = m_ColumnType.value(left.column(), QVariant::String); 50 | switch (Type) 51 | { 52 | case QVariant::Invalid: 53 | return (RightItem.type() == QVariant::Invalid); 54 | case QVariant::Date: 55 | if (LeftItem.toDate() <= RightItem.toDate()) 56 | return true; 57 | else 58 | return false; 59 | break; 60 | case QVariant::Time: 61 | if (LeftItem.toTime() <= RightItem.toTime()) 62 | return true; 63 | else 64 | return false; 65 | break; 66 | case QVariant::DateTime: 67 | if (LeftItem.toDateTime() <= RightItem.toDateTime()) 68 | return true; 69 | else 70 | return false; 71 | break; 72 | case QMetaType::Float: 73 | if (LeftItem.toFloat() <= RightItem.toFloat()) 74 | return true; 75 | else 76 | return false; 77 | break; 78 | case QVariant::Double: 79 | if (LeftItem.toDouble() <= RightItem.toDouble()) 80 | return true; 81 | else 82 | return false; 83 | break; 84 | case QVariant::Int: 85 | if (LeftItem.toInt() <= RightItem.toInt()) 86 | return true; 87 | else 88 | return false; 89 | break; 90 | case QVariant::UInt: 91 | if (LeftItem.toUInt() <= RightItem.toUInt()) 92 | return true; 93 | else 94 | return false; 95 | break; 96 | case QVariant::LongLong: 97 | if (LeftItem.toLongLong() <= RightItem.toLongLong()) 98 | return true; 99 | else 100 | return false; 101 | break; 102 | case QVariant::ULongLong: 103 | if (LeftItem.toULongLong() <= RightItem.toULongLong()) 104 | return true; 105 | else 106 | return false; 107 | break; 108 | case QVariant::Char: 109 | if (LeftItem.toChar() <= RightItem.toChar()) 110 | return true; 111 | else 112 | return false; 113 | break; 114 | case QVariant::String: 115 | if (LeftItem.toString() <= RightItem.toString()) 116 | return true; 117 | else 118 | return false; 119 | break; 120 | default: 121 | int ret = 0; 122 | if (isSortLocaleAware()) 123 | { 124 | ret = LeftItem.toString().localeAwareCompare(RightItem.toString()); 125 | if (ret <= 0) 126 | return true; 127 | else 128 | return false; 129 | } 130 | else 131 | { 132 | ret = LeftItem.toString().compare(RightItem.toString(), sortCaseSensitivity()); 133 | if(ret <= 0) 134 | return true; 135 | else 136 | return false; 137 | } 138 | } 139 | return true; 140 | } 141 | 142 | virtual bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 143 | { 144 | bool ret = true; 145 | QList columns = m_RowFilter.keys(); 146 | QList values; 147 | foreach(int col, columns) 148 | { 149 | values.append(sourceModel()->index(source_row, col, source_parent)); 150 | } 151 | foreach(QModelIndex index, values) 152 | { 153 | QStringList patterns = m_RowFilter.value(index.column()); 154 | ret = ret && patterns.contains(sourceModel()->data(index).toString()); 155 | if(!ret) 156 | { 157 | break; 158 | } 159 | } 160 | return ret; 161 | } 162 | 163 | virtual bool filterAcceptsColumn(int source_column, const QModelIndex &source_parent) const 164 | { 165 | bool ret = true; 166 | QList rows = m_ColumnFilter.keys(); 167 | QList values; 168 | foreach(int row, rows) 169 | { 170 | values.append(sourceModel()->index(row, source_column, source_parent)); 171 | } 172 | foreach(QModelIndex index, values) 173 | { 174 | QStringList patterns = m_ColumnFilter.value(index.row()); 175 | ret = ret && patterns.contains(sourceModel()->data(index).toString()); 176 | if(!ret) 177 | { 178 | break; 179 | } 180 | } 181 | return ret; 182 | } 183 | 184 | protected: 185 | QMap m_RowFilter; 186 | QMap m_ColumnFilter; 187 | QMap m_ColumnType; 188 | }; 189 | 190 | 191 | } 192 | 193 | #endif // XSORTFILTERPROXYMODEL_H 194 | -------------------------------------------------------------------------------- /Model/XTableModel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XTABLEMODEL_H 2 | #define XTABLEMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "XTableModelItem.hpp" 8 | 9 | namespace FinTechUI 10 | { 11 | 12 | 13 | class XTableModel: public QAbstractTableModel 14 | { 15 | Q_OBJECT 16 | public: 17 | explicit XTableModel(QObject* parent = NULL) 18 | { 19 | 20 | } 21 | virtual ~XTableModel() 22 | { 23 | for(int i = 0; i < m_rows.size(); i++) 24 | { 25 | XTableModelRow* modelRow = m_rows.at(i); 26 | delete modelRow; 27 | modelRow = NULL; 28 | } 29 | m_rows.clear(); 30 | } 31 | void setHeaderLabels(const QStringList& header) 32 | { 33 | m_header = header; 34 | } 35 | 36 | QStringList headerLabels() const 37 | { 38 | return m_header; 39 | } 40 | 41 | void setRowForegroundColor(int row, const QColor& color = QColor()) 42 | { 43 | if(row >= 0 && row < m_rows.size()) 44 | { 45 | XTableModelRow* modelRow = m_rows[row]; 46 | if(modelRow != NULL) 47 | { 48 | int columns = modelRow->size(); 49 | if(columns < m_header.size()) 50 | { 51 | for(int col = 0; col < columns; col++) 52 | { 53 | modelRow->at(col)->setForeground(color); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | 60 | static void setRowForegroundColor(XTableModelRow* modelRow, const QColor& color = QColor()) 61 | { 62 | if(modelRow != NULL) 63 | { 64 | int columns = modelRow->size(); 65 | for(int col = 0; col < columns; col++) 66 | { 67 | modelRow->at(col)->setForeground(color); 68 | } 69 | } 70 | } 71 | 72 | void setRowBackgroundColor(int row, const QColor& color = QColor()) 73 | { 74 | if(row >= 0 && row < m_rows.size()) 75 | { 76 | XTableModelRow* modelRow = m_rows[row]; 77 | if(modelRow != NULL) 78 | { 79 | int columns = modelRow->size(); 80 | if(columns < m_header.size()) 81 | { 82 | for(int col = 0; col < columns; col++) 83 | { 84 | modelRow->at(col)->setBackground(color); 85 | } 86 | } 87 | } 88 | } 89 | } 90 | 91 | static void setRowBackgroundColor(XTableModelRow* modelRow, const QColor& color = QColor()) 92 | { 93 | if(modelRow != NULL) 94 | { 95 | int columns = modelRow->size(); 96 | for(int col = 0; col < columns; col++) 97 | { 98 | modelRow->at(col)->setBackground(color); 99 | } 100 | } 101 | } 102 | 103 | void appendRow(XTableModelRow* modelRow) 104 | { 105 | if(modelRow != NULL && checkRow(modelRow)) 106 | { 107 | int row = m_rows.size(); 108 | beginInsertRows(QModelIndex(), row, row); 109 | m_rows.append(modelRow); 110 | endInsertRows(); 111 | } 112 | } 113 | 114 | void appendRows(const QList &modelRows) 115 | { 116 | int row = m_rows.size(); 117 | int count = modelRows.size(); 118 | if(count > 0) 119 | { 120 | beginInsertRows(QModelIndex(), row, row + count); 121 | foreach(XTableModelRow* modelRow, modelRows) 122 | { 123 | m_rows.append(modelRow); 124 | } 125 | endInsertRows(); 126 | } 127 | } 128 | 129 | void insertRow(int row, XTableModelRow *modelRow) 130 | { 131 | if(row > 0 && row <= m_rows.size()) 132 | { 133 | beginInsertRows(QModelIndex(), row, row); 134 | m_rows.insert(row, modelRow); 135 | endInsertRows(); 136 | } 137 | } 138 | 139 | void insertRows(int row, const QList& modelRows) 140 | { 141 | int count = modelRows.size(); 142 | if(row > 0 && row <= m_rows.size() && count > 0) 143 | { 144 | beginInsertRows(QModelIndex(), row, row + count); 145 | for(int i = 0; i < count; i++) 146 | { 147 | m_rows.insert(row + i, modelRows.at(i)); 148 | } 149 | endInsertRows(); 150 | } 151 | } 152 | 153 | void updateRow(XTableModelRow *modelRow) 154 | { 155 | int row = m_rows.indexOf(modelRow); 156 | if(row > -1) 157 | { 158 | memcpy(m_rows[row], modelRow, sizeof(*m_rows[row])); 159 | emit dataChanged(index(row, 0), index(row, m_header.size() - 1)); 160 | } 161 | } 162 | 163 | void updateRows(const QList &modelRows) 164 | { 165 | foreach(XTableModelRow* modelRow, modelRows) 166 | { 167 | updateRow(modelRow); 168 | } 169 | } 170 | 171 | void deleteRow(XTableModelRow *modelRow, bool release) 172 | { 173 | if(modelRow != NULL) 174 | { 175 | int row = m_rows.indexOf(modelRow); 176 | if(row > -1) 177 | { 178 | beginRemoveRows(QModelIndex(), row, row); 179 | m_rows.remove(row); 180 | endRemoveRows(); 181 | 182 | } 183 | if(release) 184 | { 185 | delete modelRow; 186 | modelRow = NULL; 187 | } 188 | } 189 | } 190 | 191 | void deleteRows(QList modelRows, bool release) 192 | { 193 | foreach(XTableModelRow* modelRow, modelRows) 194 | { 195 | deleteRow(modelRow, release); 196 | } 197 | } 198 | 199 | XTableModelRow* modelRow(unsigned int row)const 200 | { 201 | if(row < m_rows.size()) 202 | return m_rows[row]; 203 | else 204 | return NULL; 205 | } 206 | 207 | QString itemText(int row, int col)const 208 | { 209 | QString result; 210 | if(row < m_rows.size()) 211 | { 212 | XTableModelRow* modelRow = m_rows.at(row); 213 | if(col < m_header.size()) 214 | { 215 | XTableModelItem* item = modelRow->at(col); 216 | result = item->text(); 217 | } 218 | } 219 | return result; 220 | } 221 | 222 | int row(XTableModelRow *modelRow) const 223 | { 224 | int row = m_rows.indexOf(modelRow); 225 | return row; 226 | } 227 | 228 | /************************************************** 229 | * Reimplemention function 230 | * ***********************************************/ 231 | virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const 232 | { 233 | if(!index.isValid()) 234 | { 235 | return QVariant(); 236 | } 237 | int row = index.row(); 238 | int col = index.column(); 239 | XTableModelRow* modelRow = m_rows.at(row); 240 | XTableModelItem* item = modelRow->at(col); 241 | switch (role) 242 | { 243 | case Qt::DisplayRole: 244 | return item->text(); 245 | case Qt::BackgroundRole: 246 | return item->background(); 247 | case Qt::ForegroundRole: 248 | return item->foreground(); 249 | case Qt::TextAlignmentRole: 250 | return item->textAlignment(); 251 | default: 252 | return QVariant(); 253 | } 254 | } 255 | 256 | virtual int rowCount(const QModelIndex& parent = QModelIndex()) const 257 | { 258 | Q_UNUSED(parent) 259 | return m_rows.size(); 260 | } 261 | 262 | virtual int columnCount(const QModelIndex& parent = QModelIndex()) const 263 | { 264 | Q_UNUSED(parent) 265 | return m_header.size(); 266 | } 267 | 268 | virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const 269 | { 270 | if(section < 0 || orientation == Qt::Horizontal && section > m_header.size()) 271 | return QVariant(); 272 | if(role == Qt::DisplayRole) 273 | { 274 | return orientation == Qt::Horizontal ? m_header.at(section) : QString::number(section + 1); 275 | } 276 | return QVariant(); 277 | } 278 | 279 | bool setData(const QModelIndex &index, const QVariant &value, int role) 280 | { 281 | if (index.isValid() && role == Qt::EditRole) 282 | { 283 | // 编辑完成,保存数据到model,并返回true 284 | XTableModelRow* ModelRow = m_rows[index.row()]; 285 | (*ModelRow)[index.column()]->setText(value.toString()); 286 | // 重新实现setData()函数时,必须显式发出信号。 287 | emit dataChanged(index, index); 288 | return true; 289 | } 290 | return false; 291 | } 292 | 293 | Qt::ItemFlags flags(const QModelIndex& index) const 294 | { 295 | return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; //设置item可编辑 296 | } 297 | 298 | virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const 299 | { 300 | return createIndex(row, column); 301 | } 302 | protected: 303 | bool checkRow(XTableModelRow* modelRow) 304 | { 305 | return true; 306 | bool result = false; 307 | if(modelRow != NULL) 308 | { 309 | result = modelRow->size() == m_header.size(); 310 | } 311 | return result; 312 | } 313 | protected: 314 | QVector m_rows; 315 | QStringList m_header; 316 | }; 317 | 318 | 319 | } 320 | 321 | 322 | #endif // XTABLEMODEL_H 323 | -------------------------------------------------------------------------------- /Model/XTableModelItem.hpp: -------------------------------------------------------------------------------- 1 | #ifndef XTABLEMODELITEM_HPP 2 | #define XTABLEMODELITEM_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace FinTechUI 10 | { 11 | 12 | class XTableModelItem; 13 | 14 | typedef QVector XTableModelRow; 15 | 16 | class XTableModelItem 17 | { 18 | public: 19 | explicit XTableModelItem(const QString& text = "", 20 | Qt::Alignment align = Qt::AlignCenter, 21 | const QColor& background = Qt::white, 22 | const QColor& foreground = Qt::black) 23 | { 24 | m_text = text; 25 | m_align = align; 26 | m_background = background; 27 | m_foreground = foreground; 28 | } 29 | 30 | explicit XTableModelItem(int value, 31 | Qt::Alignment align = Qt::AlignCenter, 32 | const QColor& background = Qt::white, 33 | const QColor& foreground = Qt::black) 34 | { 35 | m_text = QString::number(value); 36 | m_align = align; 37 | m_background = background; 38 | m_foreground = foreground; 39 | } 40 | 41 | explicit XTableModelItem(long value, 42 | Qt::Alignment align = Qt::AlignCenter, 43 | const QColor& background = Qt::white, 44 | const QColor& foreground = Qt::black) 45 | { 46 | m_text = QString::number(value); 47 | m_align = align; 48 | m_background = background; 49 | m_foreground = foreground; 50 | } 51 | 52 | explicit XTableModelItem(double value, 53 | Qt::Alignment align = Qt::AlignCenter, 54 | const QColor& background = Qt::white, 55 | const QColor& foreground = Qt::black) 56 | { 57 | m_text = QString::number(value); 58 | m_align = align; 59 | m_background = background; 60 | m_foreground = foreground; 61 | } 62 | 63 | void setText(const QString& text) 64 | { 65 | m_text = text; 66 | } 67 | 68 | void setText(int value) 69 | { 70 | m_text = QString::number(value); 71 | } 72 | 73 | void setText(long value) 74 | { 75 | m_text = QString::number(value); 76 | } 77 | 78 | void setText(long long value) 79 | { 80 | m_text = QString::number(value); 81 | } 82 | 83 | void setText(double value) 84 | { 85 | m_text = QString::number(value); 86 | } 87 | 88 | QString text()const 89 | { 90 | return m_text; 91 | } 92 | 93 | void setTextAlignment(int alignment) 94 | { 95 | m_align = alignment; 96 | } 97 | 98 | int textAlignment()const 99 | { 100 | return m_align; 101 | } 102 | 103 | void setFont(const QFont& font) 104 | { 105 | m_font = font; 106 | } 107 | 108 | QFont font()const 109 | { 110 | return m_font; 111 | } 112 | 113 | void setBackground(const QColor& color) 114 | { 115 | m_background = color; 116 | } 117 | 118 | QColor background()const 119 | { 120 | return m_background; 121 | } 122 | 123 | void setForeground(const QColor& color) 124 | { 125 | m_foreground = color; 126 | } 127 | 128 | QColor foreground()const 129 | { 130 | return m_foreground; 131 | } 132 | 133 | protected: 134 | QString m_text; 135 | int m_align; 136 | QFont m_font; 137 | QColor m_background; 138 | QColor m_foreground; 139 | }; 140 | 141 | } 142 | 143 | #endif // XTABLEMODELITEM_HPP 144 | -------------------------------------------------------------------------------- /Model/YButtonDelegate.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YBUTTONDELEGATE_HPP 2 | #define YBUTTONDELEGATE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace FinTechUI 14 | { 15 | 16 | class YButtonDelegate : public QStyledItemDelegate 17 | { 18 | Q_OBJECT 19 | public: 20 | explicit YButtonDelegate(QString Text, QObject *parent = NULL) : QStyledItemDelegate(parent) 21 | { 22 | m_Text = Text; 23 | } 24 | void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 25 | { 26 | QStyleOptionButton* button = m_OptionButtonMap.value(index); 27 | if (button == NULL) 28 | { 29 | button = new QStyleOptionButton(); 30 | button->rect = option.rect.adjusted(1, 1, -1, -1); 31 | button->text = m_Text; 32 | button->state |= QStyle::State_Enabled; 33 | (const_cast(this))->m_OptionButtonMap.insert(index, button); 34 | } 35 | 36 | painter->save(); 37 | if (option.state & QStyle::State_Selected) 38 | { 39 | painter->fillRect(option.rect, option.palette.highlight()); 40 | } 41 | painter->restore(); 42 | painter->setBackground(QBrush(QColor("#FFA500"))); 43 | const_cast(option).backgroundBrush = QBrush(QColor("#FFA500")); 44 | QApplication::style()->drawControl(QStyle::CE_PushButton, button, painter); 45 | } 46 | 47 | bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 48 | { 49 | if (event->type() == QEvent::MouseButtonPress) 50 | { 51 | QMouseEvent* e =(QMouseEvent*)event; 52 | if (option.rect.adjusted(1, 1, -1, -1).contains(e->x(), e->y()) && m_OptionButtonMap.contains(index)) 53 | { 54 | m_OptionButtonMap.value(index)->state |= QStyle::State_Sunken; 55 | } 56 | } 57 | if (event->type() == QEvent::MouseButtonRelease) 58 | { 59 | QMouseEvent* e =(QMouseEvent*)event; 60 | if (option.rect.adjusted(1, 1, -1, -1).contains(e->x(), e->y()) && m_OptionButtonMap.contains(index)) 61 | { 62 | m_OptionButtonMap.value(index)->state &= (~QStyle::State_Sunken); 63 | emit Clicked(index); 64 | } 65 | } 66 | return true; 67 | } 68 | signals: 69 | void Clicked(const QModelIndex& index); 70 | protected: 71 | QMap m_OptionButtonMap; 72 | QString m_Text; 73 | }; 74 | 75 | } 76 | 77 | #endif // YBUTTONDELEGATE_HPP 78 | -------------------------------------------------------------------------------- /Model/YHeaderView.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YHEADERVIEW_H 2 | #define YHEADERVIEW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace FinTechUI 10 | { 11 | 12 | class YHeaderView; 13 | 14 | enum HeaderDataModelRoles 15 | { 16 | HorizontalHeaderDataRole = Qt::UserRole, 17 | VerticalHeaderDataRole = Qt::UserRole + 1 18 | }; 19 | 20 | class YHeaderViewData 21 | { 22 | public: 23 | QPointer m_HeaderModel; 24 | YHeaderViewData() 25 | { 26 | 27 | } 28 | 29 | void InitFromNewModel(int orientation, QAbstractItemModel* model) 30 | { 31 | m_HeaderModel = QPointer(); 32 | QVariant data(model->data(QModelIndex(), (orientation == Qt::Horizontal ? HorizontalHeaderDataRole : VerticalHeaderDataRole))); 33 | if(data.isValid()) 34 | { 35 | m_HeaderModel = qobject_cast(data.value()); 36 | } 37 | } 38 | 39 | QModelIndex FindRootIndex(QModelIndex index) const 40 | { 41 | while(index.parent().isValid()) 42 | index = index.parent(); 43 | return index; 44 | } 45 | 46 | QModelIndexList ParentIndexes(QModelIndex index) const 47 | { 48 | QModelIndexList indexes; 49 | while(index.isValid()) 50 | { 51 | indexes.push_front(index); 52 | index = index.parent(); 53 | } 54 | return indexes; 55 | } 56 | 57 | QModelIndex FindLeaf(const QModelIndex& curentIndex, int sectionIndex, int& curentLeafIndex) 58 | { 59 | if(curentIndex.isValid()) 60 | { 61 | int childCount = curentIndex.model()->columnCount(curentIndex); 62 | if(childCount) 63 | { 64 | for(int i = 0; i < childCount; ++i) 65 | { 66 | QModelIndex res(FindLeaf(curentIndex.child(0, i), sectionIndex, curentLeafIndex)); 67 | if(res.isValid()) 68 | return res; 69 | } 70 | } 71 | else 72 | { 73 | ++curentLeafIndex; 74 | if(curentLeafIndex == sectionIndex) 75 | return curentIndex; 76 | } 77 | } 78 | return QModelIndex(); 79 | } 80 | 81 | QModelIndex LeafIndex(int sectionIndex) 82 | { 83 | if(m_HeaderModel) 84 | { 85 | int currentLeafIndex = -1; 86 | for(int i = 0; i < m_HeaderModel->columnCount(); ++i) 87 | { 88 | QModelIndex CurrentIndex(FindLeaf(m_HeaderModel->index(0, i), sectionIndex, currentLeafIndex)); 89 | if(CurrentIndex.isValid()) 90 | return CurrentIndex; 91 | } 92 | } 93 | return QModelIndex(); 94 | } 95 | 96 | QModelIndexList SearchLeafs(const QModelIndex& curentIndex) const 97 | { 98 | QModelIndexList res; 99 | if(curentIndex.isValid()) 100 | { 101 | int childCount = curentIndex.model()->columnCount(curentIndex); 102 | if(childCount) 103 | { 104 | for(int i = 0; i < childCount; ++i) 105 | res += SearchLeafs(curentIndex.child(0, i)); 106 | } 107 | else 108 | { 109 | res.push_back(curentIndex); 110 | } 111 | } 112 | return res; 113 | } 114 | 115 | QModelIndexList Leafs(const QModelIndex& searchedIndex) const 116 | { 117 | QModelIndexList leafs; 118 | if(searchedIndex.isValid()) 119 | { 120 | int childCount = searchedIndex.model()->columnCount(searchedIndex); 121 | for(int i = 0; i < childCount; ++i) 122 | { 123 | leafs += SearchLeafs(searchedIndex.child(0, i)); 124 | } 125 | } 126 | return leafs; 127 | } 128 | 129 | void setForegroundBrush(QStyleOptionHeader& opt, const QModelIndex& index) const 130 | { 131 | QVariant foregroundBrush = index.data(Qt::ForegroundRole); 132 | if(foregroundBrush.canConvert(QVariant::Brush)) 133 | { 134 | opt.palette.setBrush(QPalette::ButtonText, qvariant_cast(foregroundBrush)); 135 | } 136 | } 137 | 138 | void setBackgroundBrush(QStyleOptionHeader& opt, const QModelIndex& index) const 139 | { 140 | QVariant backgroundBrush = index.data(Qt::BackgroundRole); 141 | if(backgroundBrush.canConvert(QVariant::Brush)) 142 | { 143 | opt.palette.setBrush(QPalette::Button, qvariant_cast(backgroundBrush)); 144 | opt.palette.setBrush(QPalette::Window, qvariant_cast(backgroundBrush)); 145 | } 146 | } 147 | 148 | QSize CellSize(const QModelIndex& leafIndex, const QHeaderView* hv, QStyleOptionHeader styleOptions) const 149 | { 150 | QSize res; 151 | QVariant variant(leafIndex.data(Qt::SizeHintRole)); 152 | if(variant.isValid()) 153 | { 154 | res = qvariant_cast(variant); 155 | } 156 | QFont fnt(hv->font()); 157 | QVariant var(leafIndex.data(Qt::FontRole)); 158 | if(var.isValid() && var.canConvert(QVariant::Font)) 159 | { 160 | fnt = qvariant_cast(var); 161 | } 162 | fnt.setBold(true); 163 | QFontMetrics fm(fnt); 164 | QSize size(fm.size(0, leafIndex.data(Qt::DisplayRole).toString())); 165 | if(leafIndex.data(Qt::UserRole).isValid()) 166 | { 167 | size.transpose(); 168 | } 169 | QSize decorationsSize(hv->style()->sizeFromContents(QStyle::CT_HeaderSection, &styleOptions, QSize(), hv)); 170 | QSize emptyTextSize(fm.size(0, "")); 171 | return res.expandedTo(size+decorationsSize-emptyTextSize); 172 | } 173 | 174 | int CurrentCellWidth(const QModelIndex& searchedIndex, const QModelIndex& leafIndex, int sectionIndex, const QHeaderView* hv) const 175 | { 176 | QModelIndexList leafsList(Leafs(searchedIndex)); 177 | if(leafsList.empty()) 178 | { 179 | return hv->sectionSize(sectionIndex); 180 | } 181 | int width = 0; 182 | int firstLeafSectionIndex = sectionIndex - leafsList.indexOf(leafIndex); 183 | for(int i = 0; i < leafsList.size(); ++i) 184 | { 185 | width += hv->sectionSize(firstLeafSectionIndex+i); 186 | } 187 | return width; 188 | } 189 | 190 | int CurrentCellLeft(const QModelIndex& searchedIndex, const QModelIndex& leafIndex, int sectionIndex, int left, const QHeaderView* hv) const 191 | { 192 | QModelIndexList leafsList(Leafs(searchedIndex)); 193 | if(!leafsList.empty()) 194 | { 195 | int n = leafsList.indexOf(leafIndex); 196 | int firstLeafSectionIndex = sectionIndex - n; 197 | --n; 198 | for(; n >= 0; --n) 199 | { 200 | left -= hv->sectionSize(firstLeafSectionIndex+n); 201 | } 202 | } 203 | return left; 204 | } 205 | 206 | int paintHorizontalCell(QPainter *painter, const QHeaderView* hv, const QModelIndex& cellIndex, const QModelIndex& leafIndex, 207 | int logicalLeafIndex, const QStyleOptionHeader& styleOptions, const QRect& sectionRect, int top) const 208 | { 209 | QStyleOptionHeader uniopt(styleOptions); 210 | setForegroundBrush(uniopt, cellIndex); 211 | setBackgroundBrush(uniopt, cellIndex); 212 | 213 | int height = CellSize(cellIndex, hv, uniopt).height(); 214 | if(cellIndex == leafIndex) 215 | { 216 | height = sectionRect.height() - top; 217 | } 218 | int left = CurrentCellLeft(cellIndex, leafIndex, logicalLeafIndex, sectionRect.left(), hv); 219 | int width = CurrentCellWidth(cellIndex, leafIndex, logicalLeafIndex, hv); 220 | 221 | QRect r(left, top, width, height); 222 | 223 | uniopt.text = cellIndex.data(Qt::DisplayRole).toString(); 224 | painter->save(); 225 | uniopt.rect = r; 226 | if(cellIndex.data(Qt::UserRole).isValid()) 227 | { 228 | hv->style()->drawControl(QStyle::CE_HeaderSection, &uniopt, painter, hv); 229 | QMatrix m; 230 | m.rotate(-90); 231 | painter->setWorldMatrix(m, true); 232 | QRect new_r(0, 0, r.height(), r.width()); 233 | new_r.moveCenter(QPoint(-r.center().y(), r.center().x())); 234 | uniopt.rect = new_r; 235 | hv->style()->drawControl(QStyle::CE_HeaderLabel, &uniopt, painter, hv); 236 | } 237 | else 238 | { 239 | hv->style()->drawControl(QStyle::CE_Header, &uniopt, painter, hv); 240 | } 241 | painter->restore(); 242 | return top + height; 243 | } 244 | 245 | void paintHorizontalSection(QPainter *painter, const QRect& sectionRect, int logicalLeafIndex, const QHeaderView* hv, 246 | const QStyleOptionHeader& styleOptions, const QModelIndex& leafIndex) const 247 | { 248 | QPointF oldBO(painter->brushOrigin()); 249 | int top = sectionRect.y(); 250 | QModelIndexList indexes(ParentIndexes(leafIndex)); 251 | for(int i = 0; i < indexes.size(); ++i) 252 | { 253 | QStyleOptionHeader realStyleOptions(styleOptions); 254 | if(i < indexes.size() - 1 && (realStyleOptions.state.testFlag(QStyle::State_Sunken) || realStyleOptions.state.testFlag(QStyle::State_On))) 255 | { 256 | QStyle::State t(QStyle::State_Sunken | QStyle::State_On); 257 | realStyleOptions.state &= (~t); 258 | } 259 | top = paintHorizontalCell(painter, hv, indexes[i], leafIndex, logicalLeafIndex, realStyleOptions, sectionRect, top); 260 | } 261 | painter->setBrushOrigin(oldBO); 262 | } 263 | 264 | int paintVerticalCell(QPainter *painter, const QHeaderView* hv, const QModelIndex& cellIndex, const QModelIndex& leafIndex, 265 | int logicalLeafIndex, const QStyleOptionHeader& styleOptions, const QRect& sectionRect, int left) const 266 | { 267 | QStyleOptionHeader uniopt(styleOptions); 268 | setForegroundBrush(uniopt, cellIndex); 269 | setBackgroundBrush(uniopt, cellIndex); 270 | 271 | int width = CellSize(cellIndex, hv, uniopt).width(); 272 | if(cellIndex == leafIndex) 273 | { 274 | width = sectionRect.width() - left; 275 | } 276 | int top = CurrentCellLeft(cellIndex, leafIndex, logicalLeafIndex, sectionRect.top(), hv); 277 | int height = CurrentCellWidth(cellIndex, leafIndex, logicalLeafIndex, hv); 278 | 279 | QRect r(left, top, width, height); 280 | 281 | uniopt.text = cellIndex.data(Qt::DisplayRole).toString(); 282 | painter->save(); 283 | uniopt.rect = r; 284 | if(cellIndex.data(Qt::UserRole).isValid()) 285 | { 286 | hv->style()->drawControl(QStyle::CE_HeaderSection, &uniopt, painter, hv); 287 | QMatrix m; 288 | m.rotate(-90); 289 | painter->setWorldMatrix(m, true); 290 | QRect new_r(0, 0, r.height(), r.width()); 291 | new_r.moveCenter(QPoint(-r.center().y(), r.center().x())); 292 | uniopt.rect = new_r; 293 | hv->style()->drawControl(QStyle::CE_HeaderLabel, &uniopt, painter, hv); 294 | } 295 | else 296 | { 297 | hv->style()->drawControl(QStyle::CE_Header, &uniopt, painter, hv); 298 | } 299 | painter->restore(); 300 | return left+width; 301 | } 302 | 303 | void paintVerticalSection(QPainter *painter, const QRect& sectionRect, int logicalLeafIndex, const QHeaderView* hv, 304 | const QStyleOptionHeader& styleOptions, const QModelIndex& leafIndex) const 305 | { 306 | QPointF oldBO(painter->brushOrigin()); 307 | int left = sectionRect.x(); 308 | QModelIndexList indexes(ParentIndexes(leafIndex)); 309 | for(int i = 0; i < indexes.size(); ++i) 310 | { 311 | QStyleOptionHeader realStyleOptions(styleOptions); 312 | if(i < indexes.size() - 1 && (realStyleOptions.state.testFlag(QStyle::State_Sunken) || realStyleOptions.state.testFlag(QStyle::State_On))) 313 | { 314 | QStyle::State t(QStyle::State_Sunken | QStyle::State_On); 315 | realStyleOptions.state &= (~t); 316 | } 317 | left = paintVerticalCell(painter, hv, indexes[i], leafIndex, logicalLeafIndex, realStyleOptions, sectionRect, left); 318 | } 319 | painter->setBrushOrigin(oldBO); 320 | } 321 | }; 322 | 323 | class YHeaderView : public QHeaderView 324 | { 325 | Q_OBJECT 326 | public: 327 | YHeaderView(Qt::Orientation orientation, QWidget* parent = 0) :QHeaderView(orientation, parent), _data(new YHeaderViewData()) 328 | { 329 | connect(this, SIGNAL(sectionResized(int, int, int)), this, SLOT(OnSectionResized(int))); 330 | } 331 | 332 | void setHeaderLabels(); 333 | 334 | ~YHeaderView() 335 | { 336 | delete _data; 337 | } 338 | 339 | void setModel(QAbstractItemModel* model) 340 | { 341 | _data->InitFromNewModel(orientation(), model); 342 | QHeaderView::setModel(model); 343 | int cnt = (orientation() == Qt::Horizontal ? model->columnCount() : model->rowCount()); 344 | if(cnt) 345 | { 346 | initializeSections(0, cnt-1); 347 | } 348 | } 349 | protected: 350 | void paintSection(QPainter* painter, const QRect& rect, int logicalIndex) const 351 | { 352 | if (rect.isValid()) 353 | { 354 | QModelIndex leafIndex(_data->LeafIndex(logicalIndex)); 355 | if(leafIndex.isValid()) 356 | { 357 | if(orientation() == Qt::Horizontal) 358 | _data->paintHorizontalSection(painter, rect, logicalIndex, this, styleOptionForCell(logicalIndex), leafIndex); 359 | else 360 | _data->paintVerticalSection(painter, rect, logicalIndex, this, styleOptionForCell(logicalIndex), leafIndex); 361 | return; 362 | } 363 | } 364 | QHeaderView::paintSection(painter, rect, logicalIndex); 365 | } 366 | QSize sectionSizeFromContents(int logicalIndex) const 367 | { 368 | if(_data->m_HeaderModel) 369 | { 370 | QModelIndex curLeafIndex(_data->LeafIndex(logicalIndex)); 371 | if(curLeafIndex.isValid()) 372 | { 373 | QStyleOptionHeader styleOption(styleOptionForCell(logicalIndex)); 374 | QSize s(_data->CellSize(curLeafIndex, this, styleOption)); 375 | curLeafIndex = curLeafIndex.parent(); 376 | while(curLeafIndex.isValid()) 377 | { 378 | if(orientation() == Qt::Horizontal) 379 | s.rheight() += _data->CellSize(curLeafIndex, this, styleOption).height(); 380 | else 381 | s.rwidth() += _data->CellSize(curLeafIndex, this, styleOption).width(); 382 | curLeafIndex = curLeafIndex.parent(); 383 | } 384 | return s; 385 | } 386 | } 387 | return QHeaderView::sectionSizeFromContents(logicalIndex); 388 | } 389 | 390 | QStyleOptionHeader styleOptionForCell(int logicalInd) const 391 | { 392 | QStyleOptionHeader opt; 393 | initStyleOption(&opt); 394 | if(window()->isActiveWindow()) 395 | { 396 | opt.state |= QStyle::State_Active; 397 | } 398 | opt.textAlignment = Qt::AlignCenter; 399 | opt.iconAlignment = Qt::AlignVCenter; 400 | opt.section = logicalInd; 401 | 402 | int visual = visualIndex(logicalInd); 403 | 404 | if (count() == 1) 405 | opt.position = QStyleOptionHeader::OnlyOneSection; 406 | else 407 | { 408 | if (visual == 0) 409 | opt.position = QStyleOptionHeader::Beginning; 410 | else 411 | opt.position = (visual == count()-1 ? QStyleOptionHeader::End : QStyleOptionHeader::Middle); 412 | } 413 | 414 | if(sectionsClickable()) 415 | { 416 | if(highlightSections() && selectionModel()) 417 | { 418 | if(orientation()==Qt::Horizontal) 419 | { 420 | if(selectionModel()->columnIntersectsSelection(logicalInd, rootIndex())) 421 | opt.state |= QStyle::State_On; 422 | if(selectionModel()->isColumnSelected(logicalInd, rootIndex())) 423 | opt.state |= QStyle::State_Sunken; 424 | } 425 | else 426 | { 427 | if(selectionModel()->rowIntersectsSelection(logicalInd, rootIndex())) 428 | opt.state |= QStyle::State_On; 429 | if(selectionModel()->isRowSelected(logicalInd, rootIndex())) 430 | opt.state |= QStyle::State_Sunken; 431 | } 432 | } 433 | } 434 | if(selectionModel()) 435 | { 436 | bool previousSelected=false; 437 | if(orientation() == Qt::Horizontal) 438 | previousSelected = selectionModel()->isColumnSelected(logicalIndex(visual - 1), rootIndex()); 439 | else 440 | previousSelected = selectionModel()->isRowSelected(logicalIndex(visual - 1), rootIndex()); 441 | bool nextSelected=false; 442 | if(orientation() == Qt::Horizontal) 443 | nextSelected = selectionModel()->isColumnSelected(logicalIndex(visual + 1), rootIndex()); 444 | else 445 | nextSelected = selectionModel()->isRowSelected(logicalIndex(visual + 1), rootIndex()); 446 | if (previousSelected && nextSelected) 447 | opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected; 448 | else 449 | { 450 | if (previousSelected) 451 | opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected; 452 | else 453 | { 454 | if (nextSelected) 455 | opt.selectedPosition = QStyleOptionHeader::NextIsSelected; 456 | else 457 | opt.selectedPosition = QStyleOptionHeader::NotAdjacent; 458 | } 459 | } 460 | } 461 | return opt; 462 | } 463 | private slots: 464 | void OnSectionResized(int logicalIndex) 465 | { 466 | if(isSectionHidden(logicalIndex)) 467 | return; 468 | 469 | QModelIndex leafIndex(_data->LeafIndex(logicalIndex)); 470 | if(leafIndex.isValid()) 471 | { 472 | QModelIndexList leafsList(_data->Leafs(_data->FindRootIndex(leafIndex))); 473 | for(int n = leafsList.indexOf(leafIndex); n > 0; --n) 474 | { 475 | --logicalIndex; 476 | 477 | int w = viewport()->width(); 478 | int h = viewport()->height(); 479 | int pos = sectionViewportPosition(logicalIndex); 480 | QRect r(pos, 0, w - pos, h); 481 | if(orientation() == Qt::Horizontal) 482 | { 483 | if (isRightToLeft()) 484 | { 485 | r.setRect(0, 0, pos + sectionSize(logicalIndex), h); 486 | } 487 | } 488 | else 489 | { 490 | r.setRect(0, pos, w, h - pos); 491 | } 492 | viewport()->update(r.normalized()); 493 | } 494 | } 495 | } 496 | private: 497 | YHeaderViewData* _data; 498 | }; 499 | 500 | } 501 | 502 | #endif // YHEADERVIEW_H 503 | -------------------------------------------------------------------------------- /Model/YHeaderViewTest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef YHEADERVIEWTEST_HPP 2 | #define YHEADERVIEWTEST_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "YHeaderView.hpp" 11 | 12 | namespace FinTechUI 13 | { 14 | 15 | class ExampleModel: public QAbstractTableModel 16 | { 17 | QStandardItemModel _horizontalHeaderModel; 18 | QStandardItemModel _verticalHeaderModel; 19 | 20 | void fillHeaderModel(QStandardItemModel& headerModel) 21 | { 22 | QStandardItem* rootItem = new QStandardItem("root"); 23 | QList l; 24 | 25 | QStandardItem* rotatedTextCell=new QStandardItem("Rotated text"); 26 | rotatedTextCell->setData(1, Qt::UserRole); 27 | l.push_back(rotatedTextCell); 28 | rootItem->appendColumn(l); 29 | 30 | l.clear(); 31 | 32 | QStandardItem* cell=new QStandardItem("level 2"); 33 | l.push_back(cell); 34 | rootItem->appendColumn(l); 35 | 36 | l.clear(); 37 | 38 | l.push_back(new QStandardItem("level 3")); 39 | cell->appendColumn(l); 40 | 41 | l.clear(); 42 | 43 | l.push_back(new QStandardItem("level 3")); 44 | cell->appendColumn(l); 45 | 46 | l.clear(); 47 | 48 | l.push_back(new QStandardItem("level 2")); 49 | rootItem->appendColumn(l); 50 | 51 | headerModel.setItem(0, 0, rootItem); 52 | } 53 | 54 | public: 55 | ExampleModel(QObject* parent=0): QAbstractTableModel(parent) 56 | { 57 | fillHeaderModel(_horizontalHeaderModel); 58 | fillHeaderModel(_verticalHeaderModel); 59 | } 60 | 61 | int rowCount(const QModelIndex& /*parent*/) const 62 | { 63 | return 5; 64 | } 65 | 66 | int columnCount(const QModelIndex& /*parent*/) const 67 | { 68 | return 4; 69 | } 70 | 71 | QVariant data(const QModelIndex& index, int role) const 72 | { 73 | if(role == HeaderDataModelRoles::HorizontalHeaderDataRole) 74 | { 75 | QVariant v; 76 | v.setValue((QObject*)&_horizontalHeaderModel); 77 | return v; 78 | } 79 | if(role==HeaderDataModelRoles::VerticalHeaderDataRole) 80 | { 81 | QVariant v; 82 | v.setValue((QObject*)&_verticalHeaderModel); 83 | return v; 84 | } 85 | if(role==Qt::DisplayRole && index.isValid()) 86 | { 87 | return QString("index(%1, %2)").arg(index.row()).arg(index.column()); 88 | } 89 | return QVariant(); 90 | } 91 | 92 | Qt::ItemFlags flags ( const QModelIndex & index ) const 93 | { 94 | return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 95 | } 96 | }; 97 | 98 | class MainWindow : public QWidget 99 | { 100 | Q_OBJECT 101 | public: 102 | MainWindow(QWidget* parent = 0) : QWidget(parent) 103 | { 104 | m_TableView = new QTableView(this); 105 | m_ExampleModel = new ExampleModel; 106 | YHeaderView* hv = new YHeaderView(Qt::Horizontal, m_TableView); 107 | hv->setSectionsClickable(true); 108 | hv->setHighlightSections(true); 109 | hv->setSectionsClickable(true); 110 | m_TableView->setHorizontalHeader(hv); 111 | hv = new YHeaderView(Qt::Vertical, m_TableView); 112 | hv->setHighlightSections(true); 113 | hv->setSectionsClickable(true); 114 | m_TableView->setVerticalHeader(hv); 115 | m_TableView->setModel(m_ExampleModel); 116 | m_TableView->resizeColumnsToContents(); 117 | m_TableView->resizeRowsToContents(); 118 | m_TableView->setSortingEnabled(true); 119 | 120 | QVBoxLayout* layout = new QVBoxLayout; 121 | layout->addWidget(m_TableView); 122 | setLayout(layout); 123 | } 124 | 125 | virtual ~MainWindow() 126 | { 127 | 128 | } 129 | 130 | private: 131 | QTableView* m_TableView; 132 | ExampleModel* m_ExampleModel; 133 | }; 134 | } 135 | 136 | #endif // YHEADERVIEWTEST_HPP 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### FinTechUI 2 | - 基于Qt封装的金融科技UI组件,支持冻结列TableView、多层次表头HeaderView、自定义排序过滤模型、自定义Button代理、自定义Progress代理、自定义ComboBox代理、自定义表格模型XTableModel、可拖拽式UI插件框架。 -------------------------------------------------------------------------------- /WindowBase.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WINDOWSBASE_HPP 2 | #define WINDOWSBASE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace FinTechUI 24 | { 25 | 26 | class CommonHelper 27 | { 28 | public: 29 | static void setStyleSheet(const QString& filepath) 30 | { 31 | //加载样式文件 32 | QFile qss(filepath); 33 | if(qss.open(QFile::ReadOnly)) 34 | { 35 | QString stylesheet = QLatin1String(qss.readAll()); 36 | QString paletteColor = stylesheet.mid(20, 7); 37 | qApp->setPalette(QPalette(QColor(paletteColor))); 38 | qApp->setStyleSheet(stylesheet); 39 | } 40 | } 41 | }; 42 | 43 | class TitleBar : public QWidget 44 | { 45 | Q_OBJECT 46 | public: 47 | explicit TitleBar(QWidget *parent = NULL) : QWidget(parent) 48 | { 49 | setFixedHeight(30); 50 | setWindowFlags(Qt::FramelessWindowHint); 51 | m_iconLabel = new QLabel(this); 52 | m_iconLabel->setFixedSize(30, 20); 53 | m_iconLabel->setScaledContents(true); 54 | m_iconLabel->setFrameShadow(QFrame::Raised); 55 | 56 | m_titleLabel = new QLabel(this); 57 | m_titleLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 58 | m_titleLabel->setFrameShadow(QFrame::Raised); 59 | 60 | m_minimizeButton = new QPushButton(this); 61 | m_minimizeButton->setFixedSize(27, 22); 62 | m_minimizeButton->setObjectName("minimizeButton"); 63 | 64 | m_maximizeButton = new QPushButton(this); 65 | m_maximizeButton->setFixedSize(27, 22); 66 | m_maximizeButton->setObjectName("maximizeButton"); 67 | 68 | m_closeButton = new QPushButton(this); 69 | m_closeButton->setFixedSize(27, 22); 70 | m_closeButton->setObjectName("closeButton"); 71 | 72 | QHBoxLayout* layout = new QHBoxLayout; 73 | layout->addWidget(m_iconLabel); 74 | layout->addStretch(1); 75 | layout->addWidget(m_titleLabel); 76 | layout->addStretch(1); 77 | layout->addWidget(m_minimizeButton); 78 | layout->addWidget(m_maximizeButton); 79 | layout->addWidget(m_closeButton); 80 | setLayout(layout); 81 | 82 | setProperty("titleBar", true); 83 | setObjectName("titleBar"); 84 | 85 | connect(m_minimizeButton, SIGNAL(clicked(bool)), this, SLOT(OnClicked())); 86 | connect(m_maximizeButton, SIGNAL(clicked(bool)), this, SLOT(OnClicked())); 87 | connect(m_closeButton, SIGNAL(clicked(bool)), this, SLOT(OnClicked())); 88 | } 89 | 90 | void setWindowTitle(const QString& title) 91 | { 92 | m_titleLabel->setAlignment(Qt::AlignCenter); 93 | m_titleLabel->setText(title); 94 | } 95 | 96 | void setTitleBarIcon(const QString& iconPath) 97 | { 98 | QPixmap map(iconPath); 99 | m_iconLabel->setPixmap(map); 100 | } 101 | 102 | protected: 103 | virtual void mouseDoubleClickEvent(QMouseEvent *event) 104 | { 105 | m_maximizeButton->click(); 106 | } 107 | 108 | virtual void mousePressEvent(QMouseEvent *event) 109 | { 110 | // 鼠标左键按下事件 111 | if (event->button() == Qt::LeftButton) 112 | { 113 | // 记录鼠标左键状态 114 | m_leftButtonPressed = true; 115 | //记录鼠标在屏幕中的位置 116 | m_start = event->globalPos(); 117 | } 118 | } 119 | 120 | virtual void mouseMoveEvent(QMouseEvent *event) 121 | { 122 | // 持续按住才做对应事件 123 | if(m_leftButtonPressed) 124 | { 125 | //将父窗体移动到父窗体原来的位置加上鼠标移动的位置:event->globalPos()-m_start 126 | parentWidget()->move(parentWidget()->geometry().topLeft() + 127 | event->globalPos() - m_start); 128 | //将鼠标在屏幕中的位置替换为新的位置 129 | m_start = event->globalPos(); 130 | } 131 | } 132 | 133 | virtual void mouseReleaseEvent(QMouseEvent *event) 134 | { 135 | // 鼠标左键释放 136 | if (event->button() == Qt::LeftButton) 137 | { 138 | // 记录鼠标状态 139 | m_leftButtonPressed = false; 140 | } 141 | } 142 | 143 | virtual bool eventFilter(QObject *obj, QEvent *event) 144 | { 145 | switch(event->type()) 146 | { 147 | //设置标题 148 | case QEvent::WindowTitleChange: 149 | { 150 | QWidget *pWidget = qobject_cast(obj); 151 | if (pWidget) 152 | { 153 | m_titleLabel->setText(pWidget->windowTitle()); 154 | return true; 155 | } 156 | } 157 | //设置图标 158 | case QEvent::WindowIconChange: 159 | { 160 | QWidget *pWidget = qobject_cast(obj); 161 | if (pWidget) 162 | { 163 | QIcon icon = pWidget->windowIcon(); 164 | m_iconLabel->setPixmap(icon.pixmap(m_iconLabel->size())); 165 | return true; 166 | } 167 | } 168 | // 窗口状态变化、窗口大小变化 169 | case QEvent::WindowStateChange: 170 | case QEvent::Resize: 171 | updateMaximize(); 172 | return true; 173 | } 174 | return QWidget::eventFilter(obj, event); 175 | } 176 | 177 | virtual void showEvent(QShowEvent* event) 178 | { 179 | this->setAttribute(Qt::WA_Mapped); 180 | QWidget::showEvent(event); 181 | } 182 | 183 | void updateMaximize() 184 | { 185 | QWidget *pWindow = parentWidget()->window(); 186 | if (pWindow->isTopLevel()) 187 | { 188 | bool bMaximize = pWindow->isMaximized(); 189 | if (bMaximize) 190 | { 191 | m_maximizeButton->setToolTip(tr("Restore")); 192 | m_maximizeButton->setProperty("maximizeProperty", "restore"); 193 | } 194 | else 195 | { 196 | m_maximizeButton->setProperty("maximizeProperty", "maximize"); 197 | m_maximizeButton->setToolTip(tr("Maximize")); 198 | } 199 | pWindow->updateGeometry(); 200 | m_maximizeButton->setStyle(QApplication::style()); 201 | } 202 | } 203 | protected slots: 204 | void OnClicked() 205 | { 206 | QPushButton *pButton = qobject_cast(sender()); 207 | QWidget *pWindow = parentWidget()->window(); 208 | if(pWindow->isTopLevel()) 209 | { 210 | if (pButton == m_minimizeButton) 211 | { 212 | pWindow->showMinimized(); 213 | } 214 | else if (pButton == m_maximizeButton) 215 | { 216 | pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMinimized(); 217 | } 218 | else if (pButton == m_closeButton) 219 | { 220 | pWindow->close(); 221 | } 222 | } 223 | } 224 | 225 | private: 226 | QLabel* m_iconLabel; 227 | QLabel* m_titleLabel; 228 | QPushButton* m_minimizeButton; 229 | QPushButton* m_maximizeButton; 230 | QPushButton* m_closeButton; 231 | QPoint m_start;//起始点 232 | QPoint m_end;//结束点 233 | bool m_leftButtonPressed;//鼠标左键按下标记 234 | }; 235 | 236 | 237 | class WindowBase : public QFrame 238 | { 239 | Q_OBJECT 240 | public: 241 | WindowBase(QWidget* parent = NULL) : QFrame(parent) 242 | { 243 | m_TitleBar = new TitleBar(this); 244 | m_ContentWidget = new QWidget(this); 245 | m_ContentWidget->setObjectName("Contents"); 246 | m_Layout = new QVBoxLayout; 247 | m_Layout->addWidget(m_TitleBar); 248 | m_Layout->addWidget(m_ContentWidget); 249 | m_Layout->setSpacing(0); 250 | m_Layout->setContentsMargins(0, 0, 0, 0); 251 | 252 | setLayout(m_Layout); 253 | #if defined(Q_OS_LINUX) 254 | setWindowFlags(windowFlags() | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint); 255 | m_TitleBar->hide(); 256 | #elif defined(Q_OS_WIN64) 257 | setWindowFlags(windowFlags() | Qt::FramelessWindowHint); 258 | #endif 259 | } 260 | 261 | void setWindowTitle(const QString& title) 262 | { 263 | #if defined(Q_OS_LINUX) 264 | QWidget::setWindowTitle(title); 265 | #elif defined(Q_OS_WIN64) 266 | m_TitleBar->setWindowTitle(title); 267 | #endif 268 | } 269 | 270 | void setTitleBarIcon(const QString& iconPath) 271 | { 272 | m_TitleBar->setTitleBarIcon(iconPath); 273 | } 274 | 275 | QWidget* contentWidget() 276 | { 277 | return m_ContentWidget; 278 | } 279 | 280 | void setWindowTitleHeight(int h) 281 | { 282 | m_TitleBar->setFixedHeight(h); 283 | } 284 | protected: 285 | QWidget* m_ContentWidget;//内容组件 286 | private: 287 | TitleBar* m_TitleBar;//标题栏 288 | QVBoxLayout* m_Layout;//布局管理器 289 | }; 290 | 291 | } 292 | 293 | 294 | #endif // WINDOWSBASE_HPP 295 | --------------------------------------------------------------------------------