https://github.com/leichaojian/qt/tree/master/spreadsheet
#include <QtGui> #include "finddialog.h" #include "gotocelldialog.h" #include "mainwindow.h" #include "sortdialog.h" #include "spreadsheet.h" MainWindow::MainWindow() { spreadsheet = new Spreadsheet; //设置为中央窗口部件 setCentralWidget(spreadsheet); createActions(); createMenus(); createContextMenu(); createToolBars(); createStatusBar(); readSettings(); findDialog = 0; //显示窗口左上角的图标 setWindowIcon(QIcon(":/images/icon.png")); setCurrentFile(""); } //closeEvent事件:中途拦截close信号,用来确定是否要关闭窗口 void MainWindow::closeEvent(QCloseEvent *event) { if (okToContinue()) { //保存设置,接收此事件 writeSettings(); event->accept(); } else { //忽略此事件 event->ignore(); } } void MainWindow::newFile() { //说明保存成功 if (okToContinue()) { //保存成功后,清空界面,设置标题 spreadsheet->clear(); setCurrentFile(""); } } void MainWindow::open() { if (okToContinue()) { //创建文件对话框(getOpenFileName)并且选择过滤后的文件名(fileName) //"Spreadsheet files (*.sp)"中Spreadsheet files为描述文字,而*.sp为过滤条件 QString fileName = QFileDialog::getOpenFileName(this, tr("Open Spreadsheet"), ".", tr("Spreadsheet files (*.sp)")); //如果文件不为空,则加载文件 if (!fileName.isEmpty()) loadFile(fileName); } } bool MainWindow::save() { //是当前文件而非新建文件 if (curFile.isEmpty()) { return saveAs(); } else { return saveFile(curFile); } } bool MainWindow::saveAs() { //getSaveFileName:得到保存的文件,如果文件存在,则会提示是否要覆盖 QString fileName = QFileDialog::getSaveFileName(this, tr("Save Spreadsheet"), ".", tr("Spreadsheet files (*.sp)")); if (fileName.isEmpty()) return false; return saveFile(fileName); } void MainWindow::find() { if (!findDialog) { findDialog = new FindDialog(this); connect(findDialog, SIGNAL(findNext(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT(findNext(const QString &, Qt::CaseSensitivity))); connect(findDialog, SIGNAL(findPrevious(const QString &, Qt::CaseSensitivity)), spreadsheet, SLOT(findPrevious(const QString &, Qt::CaseSensitivity))); } //非模态对话框--->show() findDialog->show(); //raise()使窗口成为顶层窗口 findDialog->raise(); //activateWindow()激活顶层窗口 findDialog->activateWindow(); } void MainWindow::goToCell() { GoToCellDialog dialog(this); //exec为模态对话框(这里不同于show的非模态对话框) if (dialog.exec()) { QString str = dialog.lineEdit->text().toUpper(); spreadsheet->setCurrentCell(str.mid(1).toInt() - 1, str[0].unicode() - 'A'); } } //对局部区域进行排序 void MainWindow::sort() { SortDialog dialog(this); QTableWidgetSelectionRange range = spreadsheet->selectedRange(); //选定的列('A' + 1, 'A' + 4 ==> 'B','E') dialog.setColumnRange('A' + range.leftColumn(), 'A' + range.rightColumn()); if (dialog.exec()) { SpreadsheetCompare compare; compare.keys[0] = dialog.primaryColumnCombo->currentIndex(); compare.keys[1] = dialog.secondaryColumnCombo->currentIndex() - 1; compare.keys[2] = dialog.tertiaryColumnCombo->currentIndex() - 1; compare.ascending[0] = (dialog.primaryOrderCombo->currentIndex() == 0); compare.ascending[1] = (dialog.secondaryOrderCombo->currentIndex() == 0); compare.ascending[2] = (dialog.tertiaryOrderCombo->currentIndex() == 0); spreadsheet->sort(compare); } } void MainWindow::about() { QMessageBox::about(this, tr("About Spreadsheet"), tr("<h2>Spreadsheet 1.1</h2>" "<p>Copyright © 2008 Software Inc." "<p>Spreadsheet is a small application that " "demonstrates QAction, QMainWindow, QMenuBar, " "QStatusBar, QTableWidget, QToolBar, and many other " "Qt classes.")); } void MainWindow::openRecentFile() { if (okToContinue()) { //qobject_cast动态类型转换--->这里sender()的含义是什么? QAction *action = qobject_cast<QAction *>(sender()); //通过action->data()得到文件名 if (action) loadFile(action->data().toString()); } } void MainWindow::updateStatusBar() { locationLabel->setText(spreadsheet->currentLocation()); formulaLabel->setText(spreadsheet->currentFormula()); } void MainWindow::spreadsheetModified() { setWindowModified(true); updateStatusBar(); } //一个动作(action)就是一个可以添加到任意数量的菜单和工具栏上的项 void MainWindow::createActions() { //New动作 newAction = new QAction(tr("&New"), this); newAction->setIcon(QIcon(":/images/new.png")); newAction->setShortcut(QKeySequence::New); //快捷键 newAction->setStatusTip(tr("Create a new spreadsheet file")); connect(newAction, SIGNAL(triggered()), this, SLOT(newFile())); //Open动作 openAction = new QAction(tr("&Open..."), this); openAction->setIcon(QIcon(":/images/open.png")); openAction->setShortcut(QKeySequence::Open); openAction->setStatusTip(tr("Open an existing spreadsheet file")); connect(openAction, SIGNAL(triggered()), this, SLOT(open())); //Save动作 saveAction = new QAction(tr("&Save"), this); saveAction->setIcon(QIcon(":/images/save.png")); saveAction->setShortcut(QKeySequence::Save); saveAction->setStatusTip(tr("Save the spreadsheet to disk")); connect(saveAction, SIGNAL(triggered()), this, SLOT(save())); //Save as动作 saveAsAction = new QAction(tr("Save &As..."), this); saveAsAction->setStatusTip(tr("Save the spreadsheet under a new " "name")); connect(saveAsAction, SIGNAL(triggered()), this, SLOT(saveAs())); //最近打开文件动作 for (int i = 0; i < MaxRecentFiles; ++i) { recentFileActions[i] = new QAction(this); recentFileActions[i]->setVisible(false); connect(recentFileActions[i], SIGNAL(triggered()), this, SLOT(openRecentFile())); } //Exit动作 exitAction = new QAction(tr("E&xit"), this); exitAction->setShortcut(tr("Ctrl+Q")); exitAction->setStatusTip(tr("Exit the application")); //这里close由Qt提供 connect(exitAction, SIGNAL(triggered()), this, SLOT(close())); //Cut动作 cutAction = new QAction(tr("Cu&t"), this); cutAction->setIcon(QIcon(":/images/cut.png")); cutAction->setShortcut(QKeySequence::Cut); cutAction->setStatusTip(tr("Cut the current selection's contents " "to the clipboard")); connect(cutAction, SIGNAL(triggered()), spreadsheet, SLOT(cut())); //Copy动作 copyAction = new QAction(tr("&Copy"), this); copyAction->setIcon(QIcon(":/images/copy.png")); copyAction->setShortcut(QKeySequence::Copy); copyAction->setStatusTip(tr("Copy the current selection's contents " "to the clipboard")); connect(copyAction, SIGNAL(triggered()), spreadsheet, SLOT(copy())); //Paste动作 pasteAction = new QAction(tr("&Paste"), this); pasteAction->setIcon(QIcon(":/images/paste.png")); pasteAction->setShortcut(QKeySequence::Paste); pasteAction->setStatusTip(tr("Paste the clipboard's contents into " "the current selection")); connect(pasteAction, SIGNAL(triggered()), spreadsheet, SLOT(paste())); //Delete动作 deleteAction = new QAction(tr("&Delete"), this); deleteAction->setShortcut(QKeySequence::Delete); deleteAction->setStatusTip(tr("Delete the current selection's " "contents")); connect(deleteAction, SIGNAL(triggered()), spreadsheet, SLOT(del())); selectRowAction = new QAction(tr("&Row"), this); selectRowAction->setStatusTip(tr("Select all the cells in the " "current row")); connect(selectRowAction, SIGNAL(triggered()), spreadsheet, SLOT(selectCurrentRow())); selectColumnAction = new QAction(tr("&Column"), this); selectColumnAction->setStatusTip(tr("Select all the cells in the " "current column")); connect(selectColumnAction, SIGNAL(triggered()), spreadsheet, SLOT(selectCurrentColumn())); selectAllAction = new QAction(tr("&All"), this); selectAllAction->setShortcut(QKeySequence::SelectAll); selectAllAction->setStatusTip(tr("Select all the cells in the " "spreadsheet")); //selectAll由父类QTableWidget实现 connect(selectAllAction, SIGNAL(triggered()), spreadsheet, SLOT(selectAll())); findAction = new QAction(tr("&Find..."), this); findAction->setIcon(QIcon(":/images/find.png")); findAction->setShortcut(QKeySequence::Find); findAction->setStatusTip(tr("Find a matching cell")); connect(findAction, SIGNAL(triggered()), this, SLOT(find())); goToCellAction = new QAction(tr("&Go to Cell..."), this); goToCellAction->setIcon(QIcon(":/images/gotocell.png")); goToCellAction->setShortcut(tr("Ctrl+G")); goToCellAction->setStatusTip(tr("Go to the specified cell")); connect(goToCellAction, SIGNAL(triggered()), this, SLOT(goToCell())); recalculateAction = new QAction(tr("&Recalculate"), this); recalculateAction->setShortcut(tr("F9")); recalculateAction->setStatusTip(tr("Recalculate all the " "spreadsheet's formulas")); connect(recalculateAction, SIGNAL(triggered()), spreadsheet, SLOT(recalculate())); sortAction = new QAction(tr("&Sort..."), this); sortAction->setStatusTip(tr("Sort the selected cells or all the " "cells")); connect(sortAction, SIGNAL(triggered()), this, SLOT(sort())); showGridAction = new QAction(tr("&Show Grid"), this); showGridAction->setCheckable(true); //复选动作 showGridAction->setChecked(spreadsheet->showGrid()); showGridAction->setStatusTip(tr("Show or hide the spreadsheet's " "grid")); connect(showGridAction, SIGNAL(toggled(bool)), spreadsheet, SLOT(setShowGrid(bool))); #if QT_VERSION < 0x040102 // workaround for a QTableWidget bug in Qt 4.1.1 connect(showGridAction, SIGNAL(toggled(bool)), spreadsheet->viewport(), SLOT(update())); #endif autoRecalcAction = new QAction(tr("&Auto-Recalculate"), this); autoRecalcAction->setCheckable(true); autoRecalcAction->setChecked(spreadsheet->autoRecalculate()); autoRecalcAction->setStatusTip(tr("Switch auto-recalculation on or " "off")); connect(autoRecalcAction, SIGNAL(toggled(bool)), spreadsheet, SLOT(setAutoRecalculate(bool))); aboutAction = new QAction(tr("&About"), this); aboutAction->setStatusTip(tr("Show the application's About box")); connect(aboutAction, SIGNAL(triggered()), this, SLOT(about())); aboutQtAction = new QAction(tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show the Qt library's About box")); connect(aboutQtAction, SIGNAL(triggered()), qApp, SLOT(aboutQt())); } void MainWindow::createMenus() { //addMenu用于创建一个窗口部件 fileMenu = menuBar()->addMenu(tr("&File")); fileMenu->addAction(newAction); fileMenu->addAction(openAction); fileMenu->addAction(saveAction); fileMenu->addAction(saveAsAction); separatorAction = fileMenu->addSeparator(); //分隔符 //最近打开的文件 for (int i = 0; i < MaxRecentFiles; ++i) fileMenu->addAction(recentFileActions[i]); fileMenu->addSeparator(); fileMenu->addAction(exitAction); editMenu = menuBar()->addMenu(tr("&Edit")); editMenu->addAction(cutAction); editMenu->addAction(copyAction); editMenu->addAction(pasteAction); editMenu->addAction(deleteAction); //Select是Edit菜单下的一个子菜单 selectSubMenu = editMenu->addMenu(tr("&Select")); selectSubMenu->addAction(selectRowAction); selectSubMenu->addAction(selectColumnAction); selectSubMenu->addAction(selectAllAction); editMenu->addSeparator(); editMenu->addAction(findAction); editMenu->addAction(goToCellAction); toolsMenu = menuBar()->addMenu(tr("&Tools")); toolsMenu->addAction(recalculateAction); toolsMenu->addAction(sortAction); optionsMenu = menuBar()->addMenu(tr("&Options")); optionsMenu->addAction(showGridAction); optionsMenu->addAction(autoRecalcAction); //菜单栏中增加间隔 menuBar()->addSeparator(); helpMenu = menuBar()->addMenu(tr("&Help")); helpMenu->addAction(aboutAction); helpMenu->addAction(aboutQtAction); } /************************** 任何Qt窗口部件都可以有一个与之相关联的QAction列表。要为该应用程序提供一个上下文菜单,可以将 所需要的动作添加到Spreadsheet窗口部件中,并且将那个窗口部件的上下文菜单策略(context menu policy) 设置为一个显示这些动作的上下文菜单。当用户在一个窗口部件上单击鼠标右键,或者是在键盘上按下一个与平台相关的按键时,就可以激活这些上下文菜单。 *****************************/ void MainWindow::createContextMenu() { //将菜单栏cutAction,copyAction和pasteAction关联到spreadsheet中 spreadsheet->addAction(cutAction); spreadsheet->addAction(copyAction); spreadsheet->addAction(pasteAction); spreadsheet->setContextMenuPolicy(Qt::ActionsContextMenu); } void MainWindow::createToolBars() { fileToolBar = addToolBar(tr("&File")); fileToolBar->addAction(newAction); fileToolBar->addAction(openAction); fileToolBar->addAction(saveAction); editToolBar = addToolBar(tr("&Edit")); editToolBar->addAction(cutAction); editToolBar->addAction(copyAction); editToolBar->addAction(pasteAction); editToolBar->addSeparator(); editToolBar->addAction(findAction); editToolBar->addAction(goToCellAction); } void MainWindow::createStatusBar() { locationLabel = new QLabel(" W999 "); //默认情况下是向左对齐并且垂直居中 locationLabel->setAlignment(Qt::AlignHCenter); locationLabel->setMinimumSize(locationLabel->sizeHint()); formulaLabel = new QLabel; //设定缩进大小 formulaLabel->setIndent(3); statusBar()->addWidget(locationLabel); //1为伸展因子,防止状态显示过于靠近 statusBar()->addWidget(formulaLabel, 1); connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)), this, SLOT(updateStatusBar())); //修改(modified)后更新状态栏 connect(spreadsheet, SIGNAL(modified()), this, SLOT(spreadsheetModified())); //更新状态栏 updateStatusBar(); } //读取配置---通常为键值方式 void MainWindow::readSettings() { QSettings settings("Software Inc.", "Spreadsheet"); restoreGeometry(settings.value("geometry").toByteArray()); recentFiles = settings.value("recentFiles").toStringList(); updateRecentFileActions(); bool showGrid = settings.value("showGrid", true).toBool(); showGridAction->setChecked(showGrid); bool autoRecalc = settings.value("autoRecalc", true).toBool(); autoRecalcAction->setChecked(autoRecalc); } void MainWindow::writeSettings() { QSettings settings("Software Inc.", "Spreadsheet"); settings.setValue("geometry", saveGeometry()); settings.setValue("recentFiles", recentFiles); settings.setValue("showGrid", showGridAction->isChecked()); settings.setValue("autoRecalc", autoRecalcAction->isChecked()); } bool MainWindow::okToContinue() { if (isWindowModified()) { int r = QMessageBox::warning(this, tr("Spreadsheet"), tr("The document has been modified.\n" "Do you want to save your changes?"), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel); if (r == QMessageBox::Yes) { return save(); } else if (r == QMessageBox::Cancel) { return false; } } return true; } bool MainWindow::loadFile(const QString &fileName) { //readFile从磁盘中读取文件并显示出来 if (!spreadsheet->readFile(fileName)) { statusBar()->showMessage(tr("Loading canceled"), 2000); return false; } setCurrentFile(fileName); statusBar()->showMessage(tr("File loaded"), 2000); return true; } bool MainWindow::saveFile(const QString &fileName) { if (!spreadsheet->writeFile(fileName)) { statusBar()->showMessage(tr("Saving canceled"), 2000); return false; } setCurrentFile(fileName); statusBar()->showMessage(tr("File saved"), 2000); return true; } void MainWindow::setCurrentFile(const QString &fileName) { curFile = fileName; setWindowModified(false); QString shownName = tr("Untitled"); if (!curFile.isEmpty()) { //strippedName:移除文件名中的路径字符 shownName = strippedName(curFile); //从最近打开文件中移除当前文件 recentFiles.removeAll(curFile); //将当前文件加入最近打开文件的开头 recentFiles.prepend(curFile); //更新最近打开文件 updateRecentFileActions(); } //显示标题(*代表已修改而未保存) setWindowTitle(tr("%1[*] - %2").arg(shownName) .arg(tr("Spreadsheet"))); } void MainWindow::updateRecentFileActions() { QMutableStringListIterator i(recentFiles); //用来移除任何不存在的文件 while (i.hasNext()) { if (!QFile::exists(i.next())) i.remove(); } for (int j = 0; j < MaxRecentFiles; ++j) { if (j < recentFiles.count()) { //保存最近打开文件的信息 QString text = tr("&%1 %2") .arg(j + 1) .arg(strippedName(recentFiles[j])); recentFileActions[j]->setText(text); recentFileActions[j]->setData(recentFiles[j]); recentFileActions[j]->setVisible(true); } else { recentFileActions[j]->setVisible(false); } } separatorAction->setVisible(!recentFiles.isEmpty()); } QString MainWindow::strippedName(const QString &fullFileName) { return QFileInfo(fullFileName).fileName(); }
#include <QtGui> #include "cell.h" #include "spreadsheet.h" Spreadsheet::Spreadsheet(QWidget *parent) : QTableWidget(parent) { autoRecalc = true; //将item的原型设置为Cell类型 setItemPrototype(new Cell); //选择模式为:ContiguousSelection,则允许单矩形选择框方法 setSelectionMode(ContiguousSelection); connect(this, SIGNAL(itemChanged(QTableWidgetItem *)), this, SLOT(somethingChanged())); //调用clear()来重新调整表格的尺寸大小并且设置列标题 clear(); } //得到当前位置 QString Spreadsheet::currentLocation() const { return QChar('A' + currentColumn()) + QString::number(currentRow() + 1); } //返回当前单元格的公式 QString Spreadsheet::currentFormula() const { return formula(currentRow(), currentColumn()); } //自定义selectedRange(),用于返回选中的表格范围 QTableWidgetSelectionRange Spreadsheet::selectedRange() const { QList<QTableWidgetSelectionRange> ranges = selectedRanges(); if (ranges.isEmpty()) return QTableWidgetSelectionRange(); return ranges.first(); } void Spreadsheet::clear() { //调整表格为0行0列,做到清空所有的表格内容 setRowCount(0); setColumnCount(0); //重新设置表格的大小为RowCount*ColumnCount setRowCount(RowCount); setColumnCount(ColumnCount); for (int i = 0; i < ColumnCount; ++i) { QTableWidgetItem *item = new QTableWidgetItem; item->setText(QString(QChar('A' + i))); //设置水平标题栏列i到item中 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(); } //cut = copy + del void Spreadsheet::cut() { copy(); del(); } void Spreadsheet::copy() { //由于在构造函数中setSelectionMode(ContiguousSelection);则选择范围无法大于1,需自定义selectedRange() QTableWidgetSelectionRange range = selectedRange(); QString str; //行与行之间用'\n'来分割,列与列之间用'\t'来分割 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; //这里不够智能,必须选定一样大小的范围才能粘贴上去(excel和wps只要选定一个表格即可) 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(); //标记为重新计算 } } //通过update来重新绘制整个电子表格 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 { //这里item是一个函数 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 ""; } } //返回row行,column列的数据 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; }
#include <QtGui> #include "cell.h" Cell::Cell() { //设置缓存为最新 setDirty(); } QTableWidgetItem *Cell::clone() const { return new Cell(*this); } void Cell::setData(int role, const QVariant &value) { //通过特定的模式(role)来显示数据(value) QTableWidgetItem::setData(role, value); //编辑模式(EditRole)下,单元格需要重新计算 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 { //否则,为double类型 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; }