#pragma once
#include <QTableWidget>
#include <functional>
class SimpleTable : public QTableWidget
{
Q_OBJECT
public:
typedef std::function<QWidget*(void* data)> CreateColWidgetFunc;
typedef std::function<void(void* data)> DeleteDataFunc;
explicit SimpleTable(QWidget *parent = 0);
virtual ~SimpleTable();
void init(int rowHeight, const std::vector<CreateColWidgetFunc> &createFuncs, const DeleteDataFunc &delFunc);
void initData(const std::map<quint64, void*> &data, const std::function<bool(quint64, quint64)> &sortCond = nullptr);
const std::map<quint64, void*>& constDatas() const;
std::map<quint64, void*>& datas();
const std::vector<quint64>& constIds() const;
std::vector<quint64>& ids();
std::vector<quint64> srcIds();
void* findData(int row);
void resetContent();
void refreshVisible();
std::vector<quint64> getSelectedIds() const;
void setSelectedIds(const std::vector<quint64> &ids);
void deleteDataWidget(std::vector<int> rows);
void addDataWidget(quint64 id, void* data);
void modifyDataWidget(int row);
void sort(const std::function<bool(quint64, quint64)> &compFunc);
private:
void clearData();
void deleteWidget(int row);
void setRowWidget(int row);
void setSelectedRows(const std::vector<int> &rows);
private:
std::vector<CreateColWidgetFunc> m_createColWidgetFuncs;
DeleteDataFunc m_deleteDataFunc;
std::vector<quint64> m_ids;
std::map<quint64, void*> m_datas;
bool m_isFilter;
int m_visibleCount;
int m_rowHeight;
};
#include "simpletable.h"
#include <QtWidgets>
#include <set>
#include <QDebug>
#include <QTime>
static const int s_itemFlag = 0xfff;
SimpleTable::SimpleTable(QWidget *parent)
: QTableWidget(parent), m_visibleCount(0), m_rowHeight(0), m_isFilter(false)
{
//this->horizontalHeader()->hide();
//this->verticalHeader()->hide();
//this->setSelectionBehavior(QAbstractItemView::SelectRows); // 选择行
//this->setSelectionMode(QAbstractItemView::ExtendedSelection); // 选中多个目标
//this->setEditTriggers(QAbstractItemView::NoEditTriggers); // 不能编辑
//this->verticalHeader()->setVisible(false);
//this->setFrameShape(QFrame::NoFrame);
//this->setShowGrid(false);
this->setAutoScroll(false);
connect(this->verticalScrollBar(), &QScrollBar::valueChanged, [this] (int value) {
static int oldValue = 0;
if (value > oldValue) {
if (this->rowCount() - value < m_visibleCount) {
value = this->rowCount() - m_visibleCount;
}
for (int i=oldValue; i<value; i++) {
deleteWidget(i);
}
} else {
int end = qMin(oldValue + m_visibleCount, this->rowCount());
for (int i=value + m_visibleCount; i<end; i++) {
deleteWidget(i);
}
}
int start = qMax(0, value);
int end = qMin(m_visibleCount + value, this->rowCount());
for (int i = start; i < end; i++) {
QTableWidgetItem *item = this->item(i, 0);
if (!item) {
continue;
}
int flag = item->data(Qt::UserRole + s_itemFlag).toInt();
int colCount = m_createColWidgetFuncs.size();
if (0 == flag && colCount > 0) {
this->setRowWidget(i);
}
}
oldValue = start;
});
connect(this->verticalScrollBar(), &QScrollBar::rangeChanged, [this] (int min, int max) {
if (m_rowHeight > 0) {
int newVisualCount = this->height() / m_rowHeight + 1;
if (m_visibleCount != newVisualCount) {
m_visibleCount = newVisualCount;
emit this->verticalScrollBar()->valueChanged(this->verticalScrollBar()->value());
} else {
int start = this->verticalScrollBar()->value();
int end = qMin<int>(this->rowCount(), start + m_visibleCount);
for (int i=start; i<end; i++) {
QTableWidgetItem *item = this->item(i, 0);
if (!item) {
continue;
}
if (0 == item->data(Qt::UserRole + s_itemFlag).toInt()) {
this->setRowWidget(i);
}
}
}
}
});
}
SimpleTable::~SimpleTable()
{
clearData();
}
void SimpleTable::init(int rowHeight, const std::vector<CreateColWidgetFunc> &createFuncs, const DeleteDataFunc &delFunc)
{
this->setColumnCount(createFuncs.size());
m_rowHeight = rowHeight;
m_createColWidgetFuncs = createFuncs;
m_deleteDataFunc = delFunc;
}
void SimpleTable::initData(const std::map<quint64, void*> &data, const std::function<bool(quint64, quint64)> &sortCond)
{
clearData();
m_datas = data;
auto iter = data.begin();
for ( ; iter != data.end(); ++iter) {
m_ids.push_back(iter->first);
}
if (sortCond) {
std::sort(m_ids.begin(), m_ids.end(), sortCond);
}
resetContent();
}
const std::map<quint64, void*>& SimpleTable::constDatas() const
{
return m_datas;
}
std::map<quint64, void*>& SimpleTable::datas()
{
return m_datas;
}
const std::vector<quint64>& SimpleTable::constIds() const
{
return m_ids;
}
std::vector<quint64>& SimpleTable::ids()
{
return m_ids;
}
std::vector<quint64> SimpleTable::srcIds()
{
std::vector<quint64> result;
auto iter = m_datas.begin();
for ( ; iter != m_datas.end(); ++iter) {
result.push_back(iter->first);
}
return result;
}
void* SimpleTable::findData(int row)
{
if (row >=0 && row < (int)m_ids.size()) {
return m_datas.at(m_ids.at(row));
}
return NULL;
}
void SimpleTable::resetContent()
{
this->verticalScrollBar()->setValue(0);
this->clearContents();
int rowCount = m_ids.size();
this->setRowCount(rowCount);
if (m_rowHeight > 0) {
m_visibleCount = this->height() / m_rowHeight + 1;
}
int colCount = this->columnCount();
auto citer = m_ids.cbegin();
for (int row=0; citer != m_ids.cend(); ++row, ++citer) {
this->setRowHeight(row, m_rowHeight);
for (int col=0; col<colCount; ++col) {
QTableWidgetItem *item = new QTableWidgetItem();
this->setItem(row, col, item);
if (0 == col) {
if (row < m_visibleCount) {
this->setRowWidget(row);
item->setData(Qt::UserRole + s_itemFlag, 1);
} else {
item->setData(Qt::UserRole + s_itemFlag, 0);
}
}
}
}
}
void SimpleTable::refreshVisible()
{
int start = this->verticalScrollBar()->value();
int end = qMin(start + m_visibleCount, this->rowCount());
for (int i=start; i<end; i++) {
setRowWidget(i);
}
}
std::vector<quint64> SimpleTable::getSelectedIds() const
{
QList<QTableWidgetItem*> selectedItems = this->selectedItems();
std::vector<quint64> selectedIds;
foreach (QTableWidgetItem *item, selectedItems) {
quint64 id = m_ids[item->row()];
if (selectedIds.end() == std::find(selectedIds.begin(), selectedIds.end(), id)) {
selectedIds.push_back(id);
}
}
return selectedIds;
}
void SimpleTable::setSelectedIds(const std::vector<quint64> &ids)
{
std::vector<int> newSelectedRows;
auto beginIter = m_ids.begin();
auto iter = ids.begin();
for ( ; iter != ids.end(); ++iter) {
auto findIter = std::find(m_ids.begin(), m_ids.end(), *iter);
if (findIter != m_ids.end()) {
newSelectedRows.push_back(findIter - beginIter);
}
}
this->setSelectedRows(newSelectedRows);
}
void SimpleTable::deleteDataWidget(std::vector<int> rows)
{
std::sort(rows.begin(), rows.end(), [](int left, int right) { return left > right; });
auto iter = rows.begin();
for ( ; iter != rows.end(); ++iter) {
int row = *iter;
if (row >= 0 && row < (int)m_ids.size()) {
this->removeRow(row);
m_deleteDataFunc(m_datas.at(m_ids.at(row)));
m_datas.erase(m_ids.at(row));
m_ids.erase(m_ids.begin() + row);
}
}
}
void SimpleTable::addDataWidget(quint64 id, void* data)
{
if (!data) {
return;
}
if (m_datas.find(id) != m_datas.end()) {
return;
}
m_ids.push_back(id);
m_datas[id] = data;
int colCount = this->columnCount();
int newRow = this->rowCount();
this->insertRow(newRow);
this->setRowHeight(newRow, m_rowHeight);
for (int col=0; col<colCount; ++col) {
QTableWidgetItem *item = new QTableWidgetItem();
this->setItem(newRow, col, item);
if (0 == col) {
if (newRow < m_visibleCount) {
this->setRowWidget(newRow);
item->setData(Qt::UserRole + s_itemFlag, 1);
} else {
item->setData(Qt::UserRole + s_itemFlag, 0);
}
}
}
}
void SimpleTable::modifyDataWidget(int row)
{
setRowWidget(row);
}
void SimpleTable::sort(const std::function<bool(quint64, quint64)> &compFunc)
{
if ((int)m_ids.size() < this->rowCount()) {
return;
}
std::vector<quint64> oldSelectedIds = this->getSelectedIds();
std::sort(m_ids.begin(), m_ids.end(), compFunc);
refreshVisible();
if (oldSelectedIds.size() > 0) {
this->setSelectedIds(oldSelectedIds);
}
}
void SimpleTable::clearData()
{
m_ids.clear();
auto iter = m_datas.begin();
if (m_deleteDataFunc) {
auto iter = m_datas.begin();
for ( ; iter != m_datas.end(); ++iter) {
m_deleteDataFunc(iter->second);
}
}
m_datas.clear();
}
void SimpleTable::deleteWidget(int row)
{
if (row < 0 || row >= this->rowCount()) {
return;
}
QTableWidgetItem *item = this->item(row, 0);
if (item->data(Qt::UserRole + s_itemFlag).toInt() == 1) {
int colCount = this->columnCount();
for (int col=0; col<colCount; ++col) {
this->removeCellWidget(row, col);
}
item->setData(Qt::UserRole + s_itemFlag, 0);
}
}
void SimpleTable::setRowWidget(int row)
{
if (row < 0 || row >= this->rowCount() || row >= (int)m_ids.size()) {
return;
}
QTableWidgetItem *item = this->item(row, 0);
if (item->data(Qt::UserRole + s_itemFlag).toInt() == 1) {
deleteWidget(row);
}
item->setData(Qt::UserRole + s_itemFlag, 1);
int colCount = qMin<int>(m_createColWidgetFuncs.size(), this->columnCount());
for (int col=0; col<colCount; ++col) {
void* data = m_datas.at(m_ids.at(row));
if (data) {
this->setCellWidget(row, col, m_createColWidgetFuncs[col](data));
}
}
}
void SimpleTable::setSelectedRows(const std::vector<int> &rows)
{
QItemSelectionModel *selectionModel = this->selectionModel();
QItemSelection itemSelection = selectionModel->selection();
selectionModel->clearSelection();
itemSelection.clear();
auto iter = rows.begin();
for ( ; iter != rows.end(); ++iter) {
this->selectRow(*iter);
itemSelection.merge(selectionModel->selection(), QItemSelectionModel::Select);
}
selectionModel->select(itemSelection, QItemSelectionModel::Select);
}
std::vector<SimpleTable::CreateColWidgetFunc> createColWidgetFuncs;
createColWidgetFuncs.push_back([](void* data) -> QWidget* { return new ColWidget::FirstWidget(data); });
createColWidgetFuncs.push_back([](void* data) -> QWidget* { return new ColWidget::SecondWidget(data); });
createColWidgetFuncs.push_back([](void* data) -> QWidget* { return new ColWidget::ThirdWidget(data); });
ui->tableWidget->init(ROW_WIDGET_HEIGHT, createColWidgetFuncs, [](void* data) { if (data) delete (ColWidget::Data*)data; });
ui->tableWidget->initData(initData, [this](quint64 left, quint64 right) -> bool {
const std::map<quint64, void*> &srcData = ui->tableWidget->constDatas();
Data *leftData = static_cast<Data*>(srcData.at(left));
Data *rightData = static_cast<Data*>(srcData.at(right));
if (leftData->sortAuth == rightData->sortAuth) {
return leftData->name < rightData->name;
} else {
return leftData->sortAuth > rightData->sortAuth;
}
});