简介
从Qt4——快速设计框开始到Qt4——子类化QTableWidgetItem,讲解了该应用的中每个函数的构建以及应用。详情前往这些章节里面阅读。
main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow mainWin;
mainWin.show();
return app.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QAction;
class QLabel;
class FindDialog;
class Spreadsheet;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
~MainWindow();
protected:
void closeEvent(QCloseEvent *event);
private slots:
void newFile();
void open();
bool save();
bool saveAs();
void find();
void goToCell();
void sort();
void about(); //关于
void openRecentFile(); //最近打开的文件
void updateStatusBar();
void spreadsheetModified();
private:
Ui::MainWindow *ui;
void createActions();
void createMenus();
void createContextMenu();
void createToolBars();
void createStatusBar();
void readSettings();
void writeSettings();
bool okToContinue();
bool loadFile(const QString &fileName);
bool saveFile(const QString &fileName);
void setCurrentFile(const QString &fileName);
void updateRecentFileActions();
QString strippedName(const QString &fullFileName);
Spreadsheet *spreadsheet;
FindDialog *findDialog;
QLabel *locationLabel;
QLabel *formulaLabel;
QStringList recentFiles;
QString curFile;
enum { MaxRecentFiles = 5 };
QAction *recentFileActions[MaxRecentFiles];
QAction *separatorAction;
QMenu *fileMenu;
QMenu *editMenu;
QMenu *selectSubMenu;
QMenu *toolsMenu;
QMenu *optionsMenu;
QMenu *helpMenu;
QToolBar *fileToolBar;
QToolBar *editToolBar;
QAction *newAction;
QAction *openAction;
QAction *saveAction;
QAction *saveAsAction;
QAction *exitAction;
QAction *cutAction;
QAction *copyAction;
QAction *pasteAction;
QAction *deleteAction;
QAction *selectRowAction;
QAction *selectColumnAction;
QAction *selectAllAction;
QAction *findAction;
QAction *goToCellAction;
QAction *recalculateAction;
QAction *sortAction;
QAction *showGridAction;
QAction *autoRecalcAction;
QAction *aboutAction;
QAction *aboutQtAction;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include <QtWidgets>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "finddialog.h"
#include "gotocelldialog.h"
#include "sortdialog.h"
#include "spreadsheet.h"
MainWindow::MainWindow():ui(new Ui::MainWindow)
{
ui->setupUi(this);
spreadsheet = new Spreadsheet;
setCentralWidget(spreadsheet);
createActions();
createMenus();
createContextMenu();
createToolBars();
createStatusBar();
readSettings();
findDialog = 0;
setWindowIcon(QIcon(":/images/icon.png"));
setCurrentFile("");
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *event)
{
if (okToContinue()) {
writeSettings(); //保存当前设置的内容
event->accept(); //关闭窗口,正常退出
} else {
event->ignore(); //存在未保存的更改并且用户选择了Cancel,则关闭窗口后内容不受改变
}
}
void MainWindow::newFile()
{
if (okToContinue()) {
spreadsheet->clear(); //清空电子表中的全部单元格和公式
setCurrentFile(""); //编辑没有标题的文档
}
}
void MainWindow::open()
{
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()
{
QString fileName = QFileDialog::getSaveFileName(this,
tr("Save Spreadsheet"), ".",
tr("Spreadsheet files (*.sp)"));
if (fileName.isEmpty())
return false; //用户点击cancel
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)));
}
findDialog->show();
findDialog->raise();
findDialog->activateWindow();
}
void MainWindow::goToCell()
{
GoToCellDialog dialog(this);
if (dialog.exec()) { //对话框被接受
QString str = dialog.getLineEdit(); //把当前单元格的值设置成行编辑器中的值
spreadsheet->setCurrentCell(str.mid(1).toInt() - 1,
str[0].unicode() - 'A');
}
}
void MainWindow::sort()
{
SortDialog dialog(this);
QTableWidgetSelectionRange range = spreadsheet->selectedRange();
dialog.setColumnRange('A' + range.leftColumn(),
'A' + range.rightColumn());
if (dialog.exec()) {
SpreadsheetCompare compare;
compare.keys[0] =
dialog.getPrimaryColumnComboIndex();
compare.keys[1] =
dialog.getSecondaryColumnComboIndex() - 1;
compare.keys[2] =
dialog.getTertiaryColumnComboIndex() - 1;
compare.ascending[0] =
(dialog.getPrimaryOrderComboIndex() == 0);
compare.ascending[1] =
(dialog.getSecondaryOrderComboIndex() == 0);
compare.ascending[2] =
(dialog.getTertiaryOrderComboIndex() == 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()) {
QAction *action = qobject_cast<QAction *>(sender());
if (action)
loadFile(action->data().toString());
}
}
void MainWindow::updateStatusBar()
{
locationLabel->setText(spreadsheet->currentLocation());
formulaLabel->setText(spreadsheet->currentFormula());
}
void MainWindow::spreadsheetModified()
{
setWindowModified(true);
updateStatusBar();
}
void MainWindow::createActions()
{
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()));
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()));
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()));
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()));
}
exitAction = new QAction(tr("E&xit"), this);
exitAction->setShortcut(tr("Ctrl+Q"));
exitAction->setStatusTip(tr("Exit the application"));
connect(exitAction, SIGNAL(triggered()), this, SLOT(close()));
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()));
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()));
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()));
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"));
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()
{
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);
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);
}
void MainWindow::createContextMenu()
{
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);
statusBar()->addWidget(formulaLabel, 1);
connect(spreadsheet, SIGNAL(currentCellChanged(int, int, int, int)),
this, SLOT(updateStatusBar()));
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)
{
if (!spreadsheet->readFile(fileName)) {
statusBar()->showMessage(tr("Loading canceled"), 2000);
return false; //载入失败
}
setCurrentFile(fileName); //载入成功,更新窗口标题
statusBar()->showMessage(tr("File loaded"), 2000); //显示2s
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()) {
shownName = strippedName(curFile); //移除文件名中的路径字符
recentFiles.removeAll(curFile); //从列表中移除任何已经出现过的文件名,从而避免该文件名的重复
recentFiles.prepend(curFile); //把这个文件名作为文件列表的第一项添加进去
updateRecentFileActions(); //更新File菜单中的那些条目
}
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();
}
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralWidget"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>26</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="mainToolBar">
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
</widget>
<widget class="QStatusBar" name="statusBar"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>
Spreadsheet.h
#ifndef SPREADSHEET_H
#define SPREADSHEET_H
#include <QTableWidget>
class Cell;
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; //根据给定的行和列返回一个Cell对象
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 // SPREADSHEET_H
Spreadsheet.cpp
//spreadsheet.cpp
#include <QtWidgets>
#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);//Qt_4_3 = 9
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.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
Cell.cpp
#include <QtWidgets>
#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;
}
FindDialog.h
#ifndef FINDDIALOG_H//防止对头文件的多重包含
#define FINDDIALOG_H
#include <QDialog>
//前置声明,编译过程更快
class QCheckBox;
class QLabel;
class QLineEdit;
class QPushButton;
class FindDialog : public QDialog
{
Q_OBJECT//对于使用了信号与槽的类,这个宏是必需的
public:
FindDialog(QWidget *parent = 0);//默认值为空指针,说明对话框没有父对象
signals:
//单击Find按钮时对话框发射的两个信号,Qt::CaseSensitivity cs则是指输入的大小写要敏感
void findNext(const QString &str, Qt::CaseSensitivity cs);
void findPrevious(const QString &str, Qt::CaseSensitivity cs);
private slots:
void findClicked();
void enableFindButton(const QString &text);
private:
QLabel *label;
QLineEdit *lineEdit;
QCheckBox *caseCheckBox;
QCheckBox *backwardCheckBox;
QPushButton *findButton;
QPushButton *closeButton;
};
#endif
Finddialog.cpp
#include <QtWidgets>
/*Qt5中需要将#include <QtGui>换成#include<QtWidgets>,涉及到版本升级中模块变化,具体见帮助文档
*这里的<QtWidgets>包含了我们需要用到的所有类,通过帮助文档搜索QtWidgets即可查看包含的类
*在头文件中本可以仅加入这个<QtWidgets>包含,而不需要其他<QDialog>引用和前置声明,
*然而在一个头文件中再包含一个这么大的头文件着实不是一种好的编程风格,尤其是对于比较大的工程项目更是如此。
*/
#include "finddialog.h"
FindDialog::FindDialog(QWidget *parent)
: QDialog(parent)
{
label = new QLabel(tr("Find &what:"));
//tr函数调用是把它们翻译成其他语言的标记,在每一个用户可见的字符串使用tr()函数是个很好的习惯。
//&符号用于表示快捷键,可以用来控制焦点,Alt+W选中
lineEdit = new QLineEdit;
label->setBuddy(lineEdit);
//设置行编辑器为标签的伙伴,在按下标签的快捷键时焦点转移至伙伴部件即行编辑器中,仅适用于QLabel
caseCheckBox = new QCheckBox(tr("Match &case"));
backwardCheckBox = new QCheckBox(tr("Search &backward"));
findButton = new QPushButton(tr("&Find"));
//Alt+F激活按钮
findButton->setDefault(true);
//设置Find按钮成为对话框的默认按钮(按Enter键使用按钮)
findButton->setEnabled(false);
//禁用窗口部件,显示为灰色且不能进行交互操作
closeButton = new QPushButton(tr("Close"));
connect(lineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(enableFindButton(const QString &)));
connect(findButton, SIGNAL(clicked()),
this, SLOT(findClicked()));
//Find按钮调用findClicked函数
connect(closeButton, SIGNAL(clicked()),
this, SLOT(close()));
QHBoxLayout *topLeftLayout = new QHBoxLayout;//水平布局
topLeftLayout->addWidget(label);
topLeftLayout->addWidget(lineEdit);
QVBoxLayout *leftLayout = new QVBoxLayout;//竖直布局
leftLayout->addLayout(topLeftLayout);
leftLayout->addWidget(caseCheckBox);
leftLayout->addWidget(backwardCheckBox);
QVBoxLayout *rightLayout = new QVBoxLayout;
rightLayout->addWidget(findButton);
rightLayout->addWidget(closeButton);
rightLayout->addStretch();//分隔符(弹簧)
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addLayout(leftLayout);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
setWindowTitle(tr("Find"));//设置窗口标题
setFixedHeight(sizeHint().height());
}
void FindDialog::findClicked()
{
//获取行编辑器中的文本内容
QString text = lineEdit->text();
//检查是否需要判断大小写,Qt::CaseSensitive为需要,Qt::CaseInsensitive为不需要
Qt::CaseSensitivity cs =
caseCheckBox->isChecked() ? Qt::CaseSensitive
: Qt::CaseInsensitive;
//判断Search backward选项的取值,决定发射哪个信号
if (backwardCheckBox->isChecked()) {
emit findPrevious(text, cs);
} else {
emit findNext(text, cs);
}
}
void FindDialog::enableFindButton(const QString &text)
{
findButton->setEnabled(!text.isEmpty());
}
GoToCellDialog.h
#ifndef GOTOCELLDIALOG_H
#define GOTOCELLDIALOG_H
#include <QDialog>
namespace Ui {
class GoToCellDialog;
}
class GoToCellDialog : public QDialog
{
Q_OBJECT
public:
explicit GoToCellDialog(QWidget *parent = 0);
~GoToCellDialog();
QString getLineEdit();
private slots:
void on_lineEdit_textChanged(); //控制ok按钮可用状态
private:
Ui::GoToCellDialog *ui;
};
#endif // GOTOCELLDIALOG_H
GoToCellDialog.cpp
#include "gotocelldialog.h"
#include "ui_gotocelldialog.h"
#include <QRegExpValidator>
GoToCellDialog::GoToCellDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::GoToCellDialog)
{
ui->setupUi(this);//初始化窗体
//第一个字符输入为大写或者小写字母,第二个字符为一个数字范围是1 到9,第三个字符是一个数字范围为0 到9
QRegExp regExp("[A-Za-z][1-9][0-9]{0,2}");
ui->lineEdit->setValidator(new QRegExpValidator(regExp,this)); //限制编辑框输入的范围
//连接ok、cancel按钮的信号和槽
connect(ui->okButton,SIGNAL(clicked()),this,SLOT(accept()));
connect(ui->cancelButton,SIGNAL(clicked()),this,SLOT(reject()));
}
void GoToCellDialog::on_lineEdit_textChanged()
{
ui->okButton->setEnabled(ui->lineEdit->hasAcceptableInput()); //字符有效Ok按钮则有效,否则为不可用状态
}
GoToCellDialog::~GoToCellDialog()
{
delete ui;
}
QString GoToCellDialog::getLineEdit()
{
return ui->lineEdit->text().toUpper(); //获取lineEdit的内容并将其转换为大写
}
Gotocelldialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>GoToCellDialog</class>
<widget class="QDialog" name="GoToCellDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>147</height>
</rect>
</property>
<property name="windowTitle">
<string>Go to Cell</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&Cell Location:</string>
</property>
<property name="buddy">
<cstring>lineEdit</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="okButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<tabstops>
<tabstop>lineEdit</tabstop>
<tabstop>okButton</tabstop>
<tabstop>cancelButton</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
SortDialog.h
#ifndef SORTDIALOG_H
#define SORTDIALOG_H
#include <QDialog>
namespace Ui {
class SortDialog;
}
class SortDialog : public QDialog
{
Q_OBJECT
public:
explicit SortDialog(QWidget *parent = 0);
~SortDialog();
void setColumnRange(QChar first, QChar last); //设置列的范围
int getPrimaryColumnComboIndex(); //获取Primary中Colum的值
int getSecondaryColumnComboIndex(); //获取Secondary中Column的值
int getTertiaryColumnComboIndex(); //获取Tertiary中Column中的值
int getPrimaryOrderComboIndex(); //获取Primary中Order中的值
int getSecondaryOrderComboIndex(); //获取Secondary中Order的值
int getTertiaryOrderComboIndex(); //获取Tertiary中Order的值
private:
Ui::SortDialog *ui;
};
#endif // SORTDIALOG_H
SortDialog.cpp
//sortdialog.cpp
#include <QtWidgets>
#include "sortdialog.h"
#include "ui_sortdialog.h"
#include <QDebug>
SortDialog::SortDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::SortDialog)
{
ui->setupUi(this);
//构造函数中隐藏了对话框中第二键和第三键这两个部分
ui->secondaryGroupBox->hide();
ui->tertiaryGroupBox->hide();
//把窗体布局(布局名改为layout)的SizeConstraint设置为QLayout::SetFixedSize,这样使用户不能再重新修改这个对话框的窗体大小。
//这里书上没有修改对话框栅格布局layoutName为layout,直接使用会报错,记得在ui中修改
ui->layout->setSizeConstraint(QLayout::SetFixedSize);
setColumnRange('A', 'Z');//组合框中内容设置为A到Z
}
void SortDialog::setColumnRange(QChar first, QChar last)
{
ui->primaryColumnCombo->clear(); //清空,重新加载
ui->secondaryColumnCombo->clear();
ui->tertiaryColumnCombo->clear();
ui->secondaryColumnCombo->addItem(tr("None")); //添加下拉框内容
ui->tertiaryColumnCombo->addItem(tr("None"));
//这里我的ui文件第一次出了问题,不知道错在哪里总是无法实现一致性效果,最后把样例的ui文件拿过来就没问题了。重新做了一遍可能是创建新的群组框时出现了错误
ui->primaryColumnCombo->setMinimumSize(ui->secondaryColumnCombo->sizeHint()); //使第一个下拉框和第二个相同
QChar ch = first;
while (ch <= last) {
ui->primaryColumnCombo->addItem(QString(ch));
ui->secondaryColumnCombo->addItem(QString(ch));
ui->tertiaryColumnCombo->addItem(QString(ch));
ch = ch.unicode() + 1;
}
}
SortDialog::~SortDialog()
{
delete ui;
}
int SortDialog::getPrimaryColumnComboIndex()
{
return ui->primaryColumnCombo->currentIndex(); //获取column中的当前值
}
int SortDialog::getSecondaryColumnComboIndex()
{
return ui->secondaryColumnCombo->currentIndex();
}
int SortDialog::getTertiaryColumnComboIndex()
{
return ui->tertiaryColumnCombo->currentIndex();
}
int SortDialog::getPrimaryOrderComboIndex()
{
return ui->primaryOrderCombo->currentIndex();
}
int SortDialog::getSecondaryOrderComboIndex()
{
return ui->secondaryOrderCombo->currentIndex();
}
int SortDialog::getTertiaryOrderComboIndex()
{
return ui->tertiaryOrderCombo->currentIndex();
}
Sortdialog.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SortDialog</class>
<widget class="QDialog" name="SortDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>336</width>
<height>340</height>
</rect>
</property>
<property name="windowTitle">
<string>Sort</string>
</property>
<layout class="QGridLayout" name="layout">
<item row="0" column="0">
<widget class="QGroupBox" name="primaryGroupBox">
<property name="title">
<string>&Primary Key</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Column:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="primaryColumnCombo">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>28</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="primaryOrderCombo">
<item>
<property name="text">
<string>Ascending</string>
</property>
</item>
<item>
<property name="text">
<string>Descending</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Order:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1" rowspan="2">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPushButton" name="okButton">
<property name="text">
<string>OK</string>
</property>
<property name="default">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>13</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="moreButton">
<property name="text">
<string>More</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>28</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="secondaryGroupBox">
<property name="title">
<string>&Secondary Key</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Column:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="secondaryColumnCombo">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>28</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Order:</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="secondaryOrderCombo">
<item>
<property name="text">
<string>Ascending</string>
</property>
</item>
<item>
<property name="text">
<string>Descending</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="tertiaryGroupBox">
<property name="title">
<string>&Tertiary Key</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>Column:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="tertiaryColumnCombo">
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
<item row="0" column="2">
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>28</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Order:</string>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QComboBox" name="tertiaryOrderCombo">
<item>
<property name="text">
<string>Ascending</string>
</property>
</item>
<item>
<property name="text">
<string>Descending</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>okButton</sender>
<signal>clicked()</signal>
<receiver>SortDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>258</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>112</x>
<y>137</y>
</hint>
</hints>
</connection>
<connection>
<sender>cancelButton</sender>
<signal>clicked()</signal>
<receiver>SortDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>283</x>
<y>65</y>
</hint>
<hint type="destinationlabel">
<x>167</x>
<y>152</y>
</hint>
</hints>
</connection>
<connection>
<sender>moreButton</sender>
<signal>toggled(bool)</signal>
<receiver>secondaryGroupBox</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>259</x>
<y>183</y>
</hint>
<hint type="destinationlabel">
<x>205</x>
<y>248</y>
</hint>
</hints>
</connection>
<connection>
<sender>moreButton</sender>
<signal>toggled(bool)</signal>
<receiver>tertiaryGroupBox</receiver>
<slot>setVisible(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>276</x>
<y>182</y>
</hint>
<hint type="destinationlabel">
<x>206</x>
<y>340</y>
</hint>
</hints>
</connection>
</connections>
</ui>
界面