类Spreadsheet从QTableWidget派生而来,
spreadsheet.h
#ifndef SPREADSHEET_H
#define SPREADSHEET_H
#include <QTableWidget>
#include <QMessageBox>
#include <QString>
#include <QTableWidgetSelectionRange>
#include "cell.h"
class SpreadsheetCompare;
class Spreadsheet : public QTableWidget
{
Q_OBJECT
public:
Spreadsheet(QWidget *parent = 0);
bool autoRecalculate() const { return autoRecalc; }
QString currentLocation() const;
QString currentFormula() const;
QTableWidgetSelectionRange selectedRange() const;
void clear();
bool readFile(const QString &fileName);
bool writeFile(const QString &fileName);
void sort(const SpreadsheetCompare &compare);
public slots:
void cut();
void copy();
void paste();
void del();
void selectCurrentRow();
void selectCurrentColumn();
void recalculate();
void setAutoRecalculate(bool recalc);
void findNext(const QString &str, Qt::CaseSensitivity cs);
void findPrevious(const QString &str, Qt::CaseSensitivity cs);
signals:
void modified();
private slots:
void somethingChanged();
private:
enum { MagicNumber = 0x7F51C883, RowCount = 999, ColumnCount = 26 };
Cell *cell(int row, int column) const;
QString text(int row, int column) const;
QString formula(int row, int column) const;
void setFormula(int row, int column, const QString &formula);
bool autoRecalc;
};
class SpreadsheetCompare
{
public:
bool operator()(const QStringList &row1,
const QStringList &row2) const;
enum { KeyCount = 3 };
int keys[KeyCount];
bool ascending[KeyCount];
};
#endif
实现文件(不对他怎么实现的功能做太多解释,需要仔细研究代码才能理解Qt)
spreadsheet.cpp
#include <QtGui>
#include <QApplication>
#include "cell.h"
#include "spreadsheet.h"
Spreadsheet::Spreadsheet(QWidget *parent)
: QTableWidget(parent)
{
autoRecalc = true;
setItemPrototype(new Cell);
//可以允许简单矩形选择框方法
setSelectionMode(ContiguousSelection);
connect(this, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(somethingChanged()));
//重新调整表格的尺寸大小并设置列标题
clear();
}
QString Spreadsheet::currentLocation() const
{
return QChar('A' + currentColumn()) + QString::number(currentRow() + 1);
}
QString Spreadsheet::currentFormula() const
{
return formula(currentRow(), currentColumn());
}
QTableWidgetSelectionRange Spreadsheet::selectedRange() const
{
QList<QTableWidgetSelectionRange> ranges = selectedRanges();
if (ranges.isEmpty())
return QTableWidgetSelectionRange();
return ranges.first();
}
void Spreadsheet::clear()
{
setRowCount(0);
setColumnCount(0);
setRowCount(RowCount);
setColumnCount(ColumnCount);
for (int i = 0; i < ColumnCount; ++i)
{
QTableWidgetItem *item = new QTableWidgetItem;
item->setText(QString(QChar('A' + i)));
setHorizontalHeaderItem(i, item);
}
setCurrentCell(0, 0);
}
bool Spreadsheet::readFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) {
QMessageBox::warning(this, tr("Spreadsheet"),
tr("Cannot read file %1:\n%2.")
.arg(file.fileName())
.arg(file.errorString()));
return false;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_4_3);
quint32 magic;
in >> magic;
if (magic != MagicNumber) {
QMessageBox::warning(this, tr("Spreadsheet"),
tr("The file is not a Spreadsheet file."));
return false;
}
clear();
quint16 row;
quint16 column;
QString str;
QApplication::setOverrideCursor(Qt::WaitCursor);
while (!in.atEnd()) {
in >> row >> column >> str;
setFormula(row, column, str);
}
QApplication::restoreOverrideCursor();
return true;
}
bool Spreadsheet::writeFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
QMessageBox::warning(this, tr("Spreadsheet"),
tr("Cannot write file %1:\n%2.")
.arg(file.fileName())
.arg(file.errorString()));
return false;
}
QDataStream out(&file);
//避免出现因为版本不同出现问题,可以指定版本
out.setVersion(QDataStream::Qt_4_3);
out << quint32(MagicNumber);
QApplication::setOverrideCursor(Qt::WaitCursor);
for (int row = 0; row < RowCount; ++row) {
for (int column = 0; column < ColumnCount; ++column) {
QString str = formula(row, column);
if (!str.isEmpty())
out << quint16(row) << quint16(column) << str;
}
}
QApplication::restoreOverrideCursor();
return true;
}
void Spreadsheet::sort(const SpreadsheetCompare &compare)
{
QList<QStringList> rows;
QTableWidgetSelectionRange range = selectedRange();
int i;
for (i = 0; i < range.rowCount(); ++i) {
QStringList row;
for (int j = 0; j < range.columnCount(); ++j)
row.append(formula(range.topRow() + i,
range.leftColumn() + j));
rows.append(row);
}
qStableSort(rows.begin(), rows.end(), compare);
for (i = 0; i < range.rowCount(); ++i) {
for (int j = 0; j < range.columnCount(); ++j)
setFormula(range.topRow() + i, range.leftColumn() + j,
rows[i][j]);
}
clearSelection();
somethingChanged();
}
void Spreadsheet::cut()
{
copy();
del();
}
void Spreadsheet::copy()
{
QTableWidgetSelectionRange range = selectedRange();
QString str;
for (int i = 0; i < range.rowCount(); ++i) {
if (i > 0)
str += "\n";
for (int j = 0; j < range.columnCount(); ++j) {
if (j > 0)
str += "\t";
str += formula(range.topRow() + i, range.leftColumn() + j);
}
}
QApplication::clipboard()->setText(str);
}
void Spreadsheet::paste()
{
QTableWidgetSelectionRange range = selectedRange();
QString str = QApplication::clipboard()->text();
QStringList rows = str.split('\n');
int numRows = rows.count();
int numColumns = rows.first().count('\t') + 1;
if (range.rowCount() * range.columnCount() != 1
&& (range.rowCount() != numRows
|| range.columnCount() != numColumns)) {
QMessageBox::information(this, tr("Spreadsheet"),
tr("The information cannot be pasted because the copy "
"and paste areas aren't the same size."));
return;
}
for (int i = 0; i < numRows; ++i) {
QStringList columns = rows[i].split('\t');
for (int j = 0; j < numColumns; ++j) {
int row = range.topRow() + i;
int column = range.leftColumn() + j;
if (row < RowCount && column < ColumnCount)
setFormula(row, column, columns[j]);
}
}
somethingChanged();
}
void Spreadsheet::del()
{
QList<QTableWidgetItem *> items = selectedItems();
if (!items.isEmpty()) {
foreach (QTableWidgetItem *item, items)
delete item;
somethingChanged();
}
}
void Spreadsheet::selectCurrentRow()
{
selectRow(currentRow());
}
void Spreadsheet::selectCurrentColumn()
{
selectColumn(currentColumn());
}
void Spreadsheet::recalculate()
{
for (int row = 0; row < RowCount; ++row) {
for (int column = 0; column < ColumnCount; ++column) {
if (cell(row, column))
cell(row, column)->setDirty();
}
}
viewport()->update();
}
void Spreadsheet::setAutoRecalculate(bool recalc)
{
autoRecalc = recalc;
if (autoRecalc)
recalculate();
}
void Spreadsheet::findNext(const QString &str, Qt::CaseSensitivity cs)
{
int row = currentRow();
int column = currentColumn() + 1;
while (row < RowCount) {
while (column < ColumnCount) {
if (text(row, column).contains(str, cs)) {
clearSelection();
setCurrentCell(row, column);
activateWindow();
return;
}
++column;
}
column = 0;
++row;
}
QApplication::beep();
}
void Spreadsheet::findPrevious(const QString &str,
Qt::CaseSensitivity cs)
{
int row = currentRow();
int column = currentColumn() - 1;
while (row >= 0) {
while (column >= 0) {
if (text(row, column).contains(str, cs)) {
clearSelection();
setCurrentCell(row, column);
activateWindow();
return;
}
--column;
}
column = ColumnCount - 1;
--row;
}
QApplication::beep();
}
void Spreadsheet::somethingChanged()
{
if (autoRecalc)
recalculate();
emit modified();
}
Cell *Spreadsheet::cell(int row, int column) const
{
//给定行列,返回这个单元格
return static_cast<Cell *>(item(row, column));
}
void Spreadsheet::setFormula(int row, int column,
const QString &formula)
{
Cell *c = cell(row, column);
if (!c) {
c = new Cell;
setItem(row, column, c);
}
c->setFormula(formula);
}
QString Spreadsheet::formula(int row, int column) const
{
Cell *c = cell(row, column);
if (c) {
return c->formula();
} else {
return "";
}
}
QString Spreadsheet::text(int row, int column) const
{
//返回这个单元格的文本
Cell *c = cell(row, column);
if (c) {
return c->text();
} else {
return "";
}
}
bool SpreadsheetCompare::operator()(const QStringList &row1,
const QStringList &row2) const
{
for (int i = 0; i < KeyCount; ++i) {
int column = keys[i];
if (column != -1) {
if (row1[column] != row2[column]) {
if (ascending[i]) {
return row1[column] < row2[column];
} else {
return row1[column] > row2[column];
}
}
}
}
return false;
}
Cell类派生子QTbleWidgetItem. 这实际上是一个数据类,
"cell.h"
#ifndef CELL_H
#define CELL_H
#include <QTableWidgetItem>
class Cell : public QTableWidgetItem
{
public:
Cell();
QTableWidgetItem *clone() const;
void setData(int role, const QVariant &value);
QVariant data(int role) const;
void setFormula(const QString &formula);
QString formula() const;
void setDirty();
private:
QVariant value() const;
QVariant evalExpression(const QString &str, int &pos) const;
QVariant evalTerm(const QString &str, int &pos) const;
QVariant evalFactor(const QString &str, int &pos) const;
mutable QVariant cachedValue;
mutable bool cacheIsDirty;
};
#endif
这个类的头文件并不包含 Q_OBJECT宏,因为只是一个普通的C++类,不是从QObject派生而来,所以就不让它有信号槽
cell.cpp
#include <QtGui>
#include "cell.h"
Cell::Cell()
{
setDirty();
}
QTableWidgetItem *Cell::clone() const
{
return new Cell(*this);
}
void Cell::setData(int role, const QVariant &value)
{
QTableWidgetItem::setData(role, value);
if (role == Qt::EditRole)
setDirty();
}
QVariant Cell::data(int role) const
{
if (role == Qt::DisplayRole) {
if (value().isValid()) {
return value().toString();
} else {
return "####";
}
} else if (role == Qt::TextAlignmentRole) {
if (value().type() == QVariant::String) {
return int(Qt::AlignLeft | Qt::AlignVCenter);
} else {
return int(Qt::AlignRight | Qt::AlignVCenter);
}
} else {
return QTableWidgetItem::data(role);
}
}
void Cell::setFormula(const QString &formula)
{
setData(Qt::EditRole, formula);
}
QString Cell::formula() const
{
return data(Qt::EditRole).toString();
}
void Cell::setDirty()
{
cacheIsDirty = true;
}
const QVariant Invalid;
QVariant Cell::value() const
{
if (cacheIsDirty) {
cacheIsDirty = false;
QString formulaStr = formula();
if (formulaStr.startsWith('\'')) {
cachedValue = formulaStr.mid(1);
} else if (formulaStr.startsWith('=')) {
cachedValue = Invalid;
QString expr = formulaStr.mid(1);
expr.replace(" ", "");
expr.append(QChar::Null);
int pos = 0;
cachedValue = evalExpression(expr, pos);
if (expr[pos] != QChar::Null)
cachedValue = Invalid;
} else {
bool ok;
double d = formulaStr.toDouble(&ok);
if (ok) {
cachedValue = d;
} else {
cachedValue = formulaStr;
}
}
}
return cachedValue;
}
QVariant Cell::evalExpression(const QString &str, int &pos) const
{
QVariant result = evalTerm(str, pos);
while (str[pos] != QChar::Null) {
QChar op = str[pos];
if (op != '+' && op != '-')
return result;
++pos;
QVariant term = evalTerm(str, pos);
if (result.type() == QVariant::Double
&& term.type() == QVariant::Double) {
if (op == '+') {
result = result.toDouble() + term.toDouble();
} else {
result = result.toDouble() - term.toDouble();
}
} else {
result = Invalid;
}
}
return result;
}
QVariant Cell::evalTerm(const QString &str, int &pos) const
{
QVariant result = evalFactor(str, pos);
while (str[pos] != QChar::Null) {
QChar op = str[pos];
if (op != '*' && op != '/')
return result;
++pos;
QVariant factor = evalFactor(str, pos);
if (result.type() == QVariant::Double
&& factor.type() == QVariant::Double) {
if (op == '*') {
result = result.toDouble() * factor.toDouble();
} else {
if (factor.toDouble() == 0.0) {
result = Invalid;
} else {
result = result.toDouble() / factor.toDouble();
}
}
} else {
result = Invalid;
}
}
return result;
}
QVariant Cell::evalFactor(const QString &str, int &pos) const
{
QVariant result;
bool negative = false;
if (str[pos] == '-') {
negative = true;
++pos;
}
if (str[pos] == '(') {
++pos;
result = evalExpression(str, pos);
if (str[pos] != ')')
result = Invalid;
++pos;
} else {
QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
QString token;
while (str[pos].isLetterOrNumber() || str[pos] == '.') {
token += str[pos];
++pos;
}
if (regExp.exactMatch(token)) {
int column = token[0].toUpper().unicode() - 'A';
int row = token.mid(1).toInt() - 1;
Cell *c = static_cast<Cell *>(
tableWidget()->item(row, column));
if (c) {
result = c->value();
} else {
result = 0.0;
}
} else {
bool ok;
result = token.toDouble(&ok);
if (!ok)
result = Invalid;
}
}
if (negative) {
if (result.type() == QVariant::Double) {
result = -result.toDouble();
} else {
result = Invalid;
}
}
return result;
}
文件结构:
补充整个工程(未粘贴出来的部分这里都有):点击打开链接
(有什么问题可以联系我)