用Qt5实现的多文档文字编辑软件,支持新建/打开/保存/另存为/打印/打印预览、撤销/重做、剪切/复制/粘贴、文字加粗/斜体/下划线、段落对齐、段落编号/标号、字体和字号选择、颜色设置等常见功能。主要分为以下几部分,
main.cpp:应用程序入口
myword.h/.cpp:主窗口类(菜单栏、工具栏、状态栏、多文档管理)
mychild.h/.cpp:子窗口类(具体的文档内容编辑逻辑,继承QTextEdit)
1. main.cpp
//main.cpp
#include "myword.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MyWord w;
w.show();
return a.exec();
}
程序入口,初始化QApplicatio来整个Qt应用的生命周期,创建主窗口MyWord,并启动事件循环a.exec(),等待用户操作。
2. mychild.h /.cpp
MyChild类继承自QTextEdit,是实际的文档编辑窗口。一个MyChild就是一个子文档,支持富文本、文件加载保存、文字和段落格式等操作。
//mychild.h
#ifndef MYCHILD_H
#define MYCHILD_H
#include <QPrinter>
#include <QTextEdit>
#include <QtWidgets>
class MyChild : public QTextEdit {
Q_OBJECT
public:
MyChild();
void newFile(); // 新建word文件
bool loadFile(const QString &fileName); // 导入文件
bool save(); // 保存
bool saveAs(); // 另存为
bool saveFile(QString fileName); // 保存文件
QString userFriendlyCurrentFile(); // 用户友好型文件(名)
QString currentFile() { return curFile; } // 当前文件
void mergeFormatOnWordOrSelection(const QTextCharFormat &format); // 格式字体
void setAlign(int align); // 段落对齐
void setStyle(int style); // 段落标号、编号
protected:
void closeEvent(QCloseEvent *event); // 关闭事件
private slots:
void documentWasModified(); // 修改状态
private:
bool maybeSave(); // 是否保存
void setCurrentFile(const QString &fileName); // 设置当前文件
QString strippedName(const QString &fullFileName); // 提取文件名(不包含路径)
QString curFile; // 当前文件
bool isUntitled; // 文件命名状态
};
#endif // MYCHILD_H
文件操作相关:newFile()、loadFile()、maybeSave()等
文件状态相关:isUntitled()、documentWasModified()、closeEvent()
格式相关:mergeFormatOnWordOrSelection()、setAlign()等
//mychild.cpp
#include "mychild.h"
MyChild::MyChild() {
setAttribute(Qt::WA_DeleteOnClose); // 关闭窗口时销毁,
isUntitled = true;
}
void MyChild::newFile() // 新建文件
{
static int sequenceNumber = 1;
isUntitled = true;
curFile = tr("Word文档-%1").arg(sequenceNumber++);
setWindowTitle(curFile);
}
bool MyChild::loadFile(const QString &fileName) // 导入文件
{
if (!fileName.isEmpty()) {
if (!QFile::exists(fileName))
return false;
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return false;
// QByteArray可完整存储原始字节数据(包括“\ 0” )
// 不像传统的 char * 字符串把 \0 当成“字符串结束符”,一旦遇到就停止
QByteArray data = file.readAll();
QTextCodec *codec = Qt::codecForHtml(data);
QString str = codec->toUnicode(data);
if (Qt::mightBeRichText(str)) { // //富文本设置为HTml
this->setHtml(str);
} else { // 非富文本设置为简单字符串格式
str = QString::fromLocal8Bit(data);
this->setPlainText(str);
}
setCurrentFile(fileName);
connect(document(), SIGNAL(contentsChanged()), this,
SLOT(documentWasModified()));
return true;
}
}
void MyChild::documentWasModified() {
// 若文件有修改,设置相关记号
setWindowModified(document()->isModified());
}
QString MyChild::strippedName(
const QString &fullFileName) // 提取文件名称(用户友好型当前文件名)
{
return QFileInfo(fullFileName).fileName();
}
QString MyChild::userFriendlyCurrentFile() // 用户友好型当前文件
{
return strippedName(curFile);
}
void MyChild::setCurrentFile(const QString &fileName) // 设置当前文件的状态
{
curFile = QFileInfo(fileName).canonicalFilePath();
isUntitled = false;
document()->setModified(false);
setWindowModified(false);
setWindowTitle(userFriendlyCurrentFile() + "[*]");
}
void MyChild::closeEvent(QCloseEvent *event) {
if (maybeSave()) {
event->accept();
} else {
event->ignore();
}
}
bool MyChild::maybeSave() // 是否保存
{
if (!document()->isModified())
return true;
QMessageBox::StandardButton ret;
ret = QMessageBox::warning(
this, tr("Myself Qt Word"),
tr("文档'%1'已被修改,是否保存?").arg(userFriendlyCurrentFile()),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
if (ret == QMessageBox::Save)
return save(); // 用户选择"保存"
else if (ret == QMessageBox::Cancel)
return false; // 用户选择"取消"
return true; // 用户选择"丢弃/不保存"
}
bool MyChild::save() // 保存
{
if (isUntitled) {
return saveAs();
} else {
return saveFile(curFile);
}
}
bool MyChild::saveAs() // 另存为
{
QString fileName = QFileDialog::getSaveFileName(
this, tr("另存为"), curFile,
tr("HTML 文档 (*.htm *.html);;所有文件 (*.*)"));
if (fileName.isEmpty())
return false;
return saveFile(fileName);
}
bool MyChild::saveFile(QString fileName) // 保存文件
{
if (!(fileName.endsWith(".htm", Qt::CaseInsensitive) ||
fileName.endsWith(".html", Qt::CaseInsensitive))) {
fileName += ".html"; // 默认保存为 HTML 文档
}
QTextDocumentWriter writer(fileName);
bool success = writer.write(this->document());
if (success)
setCurrentFile(fileName);
return success;
}
// 字符格式(字体、颜色、字符间距等等)设置
void MyChild::mergeFormatOnWordOrSelection(const QTextCharFormat &format) {
QTextCursor cursor = this->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
this->mergeCurrentCharFormat(format);
}
// 段落对齐
void MyChild::setAlign(int align) {
if (align == 1)
this->setAlignment(Qt::AlignLeft | Qt::AlignAbsolute); // 靠左对齐
else if (align == 2)
this->setAlignment(Qt::AlignHCenter); // 水平方向居中
else if (align == 3)
this->setAlignment(Qt::AlignRight | Qt::AlignAbsolute); // 靠右对齐
else if (align == 4)
this->setAlignment(Qt::AlignJustify); // 两端对齐。
}
// 段落、列表样式
void MyChild::setStyle(int style) {
QTextCursor cursor = this->textCursor();
if (style != 0) {
QTextListFormat::Style stylename =
QTextListFormat::ListDisc; // # 样式为圆圈
switch (style) {
default:
case 1:
stylename = QTextListFormat::ListDisc; // # 样式为圆圈
break;
case 2:
stylename = QTextListFormat::ListCircle; // # 样式为空心圆圈
break;
case 3:
stylename = QTextListFormat::ListSquare; // 样式为方块
break;
case 4:
stylename = QTextListFormat::ListDecimal; // 小写阿拉伯数字,最大4999
break;
case 5:
stylename = QTextListFormat::ListLowerAlpha; // 小写拉丁字符,按字母顺序
break;
case 6:
stylename = QTextListFormat::ListUpperAlpha; // 大写拉丁字符,按字母顺序
break;
case 7:
stylename = QTextListFormat::ListLowerRoman; // 小写罗马数字,最大4999
break;
case 8:
stylename = QTextListFormat::ListUpperRoman; // 大写罗马数字,最大4999
break;
}
// 段落、列表样式的修改相互依赖,需要进行原子操作
cursor.beginEditBlock();
// 设置段落样式
QTextBlockFormat blockFmt = cursor.blockFormat();
QTextListFormat listFmt;
if (cursor.currentList()) {
listFmt = cursor.currentList()->format();
} else {
blockFmt.setIndent(0);
cursor.setBlockFormat(blockFmt);
listFmt.setIndent(blockFmt.indent() + 1);
}
listFmt.setStyle(stylename);
cursor.createList(listFmt);
cursor.endEditBlock();
} else {
QTextBlockFormat bfmt;
bfmt.setObjectIndex(-1);
cursor.mergeBlockFormat(bfmt);
}
}
对.h文件中声明的函数进行实现。
bool MyChild::loadFile(const QString &fileName) // 导入文件
{
...
QByteArray data = file.readAll();
QTextCodec *codec = Qt::codecForHtml(data);
QString str = codec->toUnicode(data);
if (Qt::mightBeRichText(str)) { // //富文本设置为HTml
this->setHtml(str);
} else { // 非富文本设置为简单字符串格式
str = QString::fromLocal8Bit(data);
this->setPlainText(str);
}
setCurrentFile(fileName);
connect(document(), SIGNAL(contentsChanged()), this,
SLOT(documentWasModified()));
return true;
}
}
文件数据读入data(QByteArray能完整保存\0在内的原始字节,不会像传统的 char* 在遇到 \0就提前终止),随后默认把内容当作 HTML(富文本)来处理。使用 codecForHtml() 试图识别字节流data的字符编码方案(例如 UTF-8等),再用获得的编码方案通过toUnicode() 解码,得到(QString)str。
接着判断 str 是否为富文本:如果是,调用 setHtml() 按 HTML 渲染显示。如果不是,则说明内容是普通文本。由于 codecForHtml() 对非 HTML 可能导致乱码。因此必须重新按本地编码(fromLocal8Bit)解码一次原始数据,再调用 setPlainText() 以纯文本形式显示。最后用connect()关联相应槽函数,文档内容发生变化时,自动调用 documentWasModified() 槽函数。
bool MyChild::saveFile(QString fileName) // 保存文件
{
if (!(fileName.endsWith(".htm", Qt::CaseInsensitive) ||
fileName.endsWith(".html", Qt::CaseInsensitive))) {
fileName += ".html"; // 默认保存为 HTML 文档
}
QTextDocumentWriter writer(fileName);
bool success = writer.write(this->document());
if (success)
setCurrentFile(fileName);
return success;
}
检查文件名。若没有 .htm/.html 后缀,补上 .html,默认保存为 HTML文件。随后通过 QTextDocumentWriter对象将当前文本编辑器的 document() 写入指定文件。写入成功后调用 setCurrentFile(fileName) 更新当前文件名。
bool MyChild::save() // 保存
{
if (isUntitled) {
return saveAs();
} else {
return saveFile(curFile);
}
}
负责决策逻辑。判断文件是否已保存过,决定调用哪个具体的保存方法。具体实现由saveAs()、saveFile()负责。
bool MyChild::maybeSave() // 是否保存
{
...
QMessageBox::StandardButton ret;
ret = QMessageBox::warning(
this, tr("Myself Qt Word"),
tr("文档'%1'已被修改,是否保存?").arg(userFriendlyCurrentFile()),
QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
if (ret == QMessageBox::Save)
...
}
若文档已修改,用QMessageBox::StandardButton变量ret保存用户在warning()中的选择,最后根据ret分支执行。
// 字符格式(字体、颜色、字符间距等等)设置
void MyChild::mergeFormatOnWordOrSelection(
const QTextCharFormat &format)
{
QTextCursor cursor = this->textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
this->mergeCurrentCharFormat(format);
}
用textCursor()获取当前的QTextCursor光标。若光标没有选中内容,默认选择光标所在位置。调用mergeCharFormat(format)在光标选择的区域应用新的字符格式(字体、颜色等)。mergeCurrentCharFormat()更新当前字符格式,设置当前光标位置的字符格式、为后续输入的文本设置默认格式。
void MyChild::setStyle(int style) {
QTextCursor cursor = this->textCursor();
if (style != 0) {
QTextListFormat::Style stylename =
QTextListFormat::ListDisc; // # 样式为圆圈
switch (style) {
default:
case 1:
stylename = QTextListFormat::ListDisc; // # 样式为圆圈
break;
...
}
// 段落、列表样式的修改相互依赖,需要进行原子操作
cursor.beginEditBlock();
// 设置段落样式
QTextBlockFormat blockFmt = cursor.blockFormat();
QTextListFormat listFmt;
if (cursor.currentList()) {
listFmt = cursor.currentList()->format();
} else {
blockFmt.setIndent(0);
cursor.setBlockFormat(blockFmt);
listFmt.setIndent(blockFmt.indent() + 1);
}
listFmt.setStyle(stylename);
cursor.createList(listFmt);
cursor.endEditBlock();
} else {
QTextBlockFormat bfmt;
bfmt.setObjectIndex(-1);
cursor.mergeBlockFormat(bfmt);
}
}
根据传入的style选择相应的QTextListFormat::Style(圆点、方块等)。若传入无效值,默认选择"样式为圆圈"。style=0时,清除列表样式,恢复为普通段落。
因为段落缩进和列表缩进紧密关联,使用cursor.beginEditBlock()与cursor.endEditBlock()包裹整个修改过程,保证段落、列表样式的修改是一个原子操作。若当前已有列表,继承其格式。若没有,重置段落缩进,建立新的列表格式。最后调用listFmt.setStyle(stylename)设置选中的样式,再执行cursor.createList(listFmt)将其应用到文本。
void MyChild::setCurrentFile(const QString &fileName) // 设置当前文件的状态
{
curFile = QFileInfo(fileName).canonicalFilePath();
isUntitled = false;
document()->setModified(false);
setWindowModified(false);
setWindowTitle(userFriendlyCurrentFile() + "[*]");
}
设置当前文件状态,在saveAs()、saveFile()、loadFIle()这些完成文件操作的函数中被调用。
setWindowTitle(userFriendlyCurrentFile()+"[*]")是Qt的特殊机制。[*]是占位符,只有当文件被修改时才会显示。
3. myword.h / .cpp
MyWord是整个应用的主窗口类,继承自QMainWindow。负责菜单栏、工具栏、状态栏的创建,以及多文档(MDI)管理。
//myword.h
#ifndef MYWORD_H
#define MYWORD_H
#include <QMainWindow>
// 第二步:添加相关头文件
#include "mychild.h"
#include <QPrintDialog>
#include <QPrintPreviewDialog>
#include <QPrinter>
class MyWord : public QMainWindow {
Q_OBJECT
public:
MyWord(QWidget *parent = nullptr);
~MyWord();
protected:
void closeEvent(QCloseEvent *event);
private slots:
void fileNew(); // 新建
void fileOpen(); // 打开
void fileSave(); // 保存
void fileSaveAs(); // 另存为
void filePrint(); // 打印
void filePrintPreview(); // 文件打印预览
void printPreview(QPrinter *); // 打印预览
void undo(); // 撤消
void redo(); // 重做
void cut(); // 剪切
void copy(); // 复制
void paste(); // 粘贴
void about(); // 关于
void textBold(); // 加粗
void textItalic(); // 斜体
void textUnderline(); // 下划线
void textAlign(QAction *a); // 文本对齐判断函数
void textStyle(int styleIndex); // 文本样式
void textFamily(const QString &f); // 文本族
void textSize(const QString &p); // 文本大小
void textColor(); // 文本颜色
void updateMenus(); // 更新菜单
void updateWindowMenu(); // 更新窗口菜单
MyChild *createMyChild(); // 创建子窗口对象
void setActiveSubWindow(QWidget *window); // 设置active激活窗口
private:
void createActions(); // 创建菜单动作
void createMenus(); // 创建菜单
void createToolBars(); // 创建工具条
void createStatusBar(); // 创建状态条
void enabledText(); // 使得【格式】下的各个子菜单项可用
void fontChanged(const QFont &f); // 字体更改
void colorChanged(const QColor &c); // 颜色更改
void alignmentChanged(Qt::Alignment a); // 对齐判断
MyChild *activeMyChild(); // 激活子窗口
QMdiSubWindow *findMyChild(const QString &fileName); // 查找子窗口
QMdiArea *mdiArea; // 多文档界面区域,管理子窗口
// 窗口菜单信号映射器,处理子窗口切换
QSignalMapper *windowMapper;
// 菜单
QMenu *fileMenu; // 文件菜单
QMenu *editMenu; // 编辑菜单
QMenu *formatMenu; // 格式菜单
QMenu *fontMenu; // 字体子菜单:加粗 倾斜 下划线
QMenu *alignMenu; // 对齐子菜单:左 右 居中 两端
QMenu *windowMenu; // 窗口菜单
QMenu *helpMenu; // 帮助菜单
// 工具栏
QToolBar *fileToolBar; // 文件工具条
QToolBar *editToolBar; // 编辑工具条
QToolBar *formatToolBar; // 格式工具条
QToolBar *comboToolBar; // 组合工具条
QComboBox *comboStyle; // 子控件 标准组合框
QFontComboBox *comboFont; // 子控件 字体组合框
QComboBox *comboSize; // 子控件 字体大小组合框
// 动作(Action)
QAction *newAct; // 【文件】主菜单 新建
QAction *openAct; // 打开
QAction *saveAct; // 保存
QAction *saveAsAct; // 另存为
QAction *printAct; // 打印
QAction *printPreviewAct; // 打印预览
QAction *exitAct; // 退出
QAction *undoAct; // 【编辑】主菜单 撤消
QAction *redoAct; // 重做
QAction *cutAct; // 剪切
QAction *copyAct; // 复制
QAction *pasteAct; // 粘贴
QAction *boldAct; // 【格式】主菜单 加粗
QAction *italicAct; // 斜体
QAction *underlineAct; // 下划画
QAction *leftAlignAct; // 左对齐
QAction *centerAct; // 居中对象
QAction *rightAlignAct; // 右对象
QAction *justifyAct; // 两端
QAction *colorAct; // 颜色
QAction *closeAct; // 【窗口】主菜单 关闭
QAction *closeAllAct; // 关闭所有
QAction *tileAct; // 平铺
QAction *cascadeAct; // 层叠
QAction *nextAct; // 下一个
QAction *previousAct; // 上一个
QAction *separatorAct; // 分离动作
QAction *aboutAct; // 【帮助】主菜单 关于
QAction *aboutQtAct; // 关于Qt
};
#endif // MYWORD_H
声明了文件操作、编辑功能、文本格式、窗口管理等等所需的函数和对象。
//myword.cpp
#include "myword.h"
#include "mychild.h"
#include <QtWidgets>
const QString srcpaths = ":/new/prefix1/images";
MyWord::MyWord(QWidget *parent) : QMainWindow(parent) {
setWindowIcon(QIcon(srcpaths + "/wordlogo2.png"));
mdiArea = new QMdiArea;
mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setCentralWidget(mdiArea);
connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)), this,
SLOT(updateMenus()));
windowMapper = new QSignalMapper(this);
connect(windowMapper, SIGNAL(mapped(QWidget *)), this,
SLOT(setActiveSubWindow(QWidget *)));
createActions(); // 创建菜单操作(动作)
createMenus();
createToolBars();
createStatusBar();
updateMenus();
resize(900, 600);
setWindowTitle(tr("Offce办公自动化文字编辑软件 V2.1"));
setUnifiedTitleAndToolBarOnMac(true);
}
MyWord::~MyWord() {}
void MyWord::closeEvent(QCloseEvent *event) {
mdiArea->closeAllSubWindows();
if (mdiArea->currentSubWindow()) {
event->ignore();
} else {
event->accept();
}
}
void MyWord::updateMenus() // 更新菜单
{
// 至少有一个子文档打开着的情况
bool hasMyChild = activeMyChild();
saveAct->setEnabled(hasMyChild);
saveAsAct->setEnabled(hasMyChild);
printAct->setEnabled(hasMyChild);
printPreviewAct->setEnabled(hasMyChild);
pasteAct->setEnabled(hasMyChild);
closeAct->setEnabled(hasMyChild);
closeAllAct->setEnabled(hasMyChild);
tileAct->setEnabled(hasMyChild);
cascadeAct->setEnabled(hasMyChild);
nextAct->setEnabled(hasMyChild);
previousAct->setEnabled(hasMyChild);
separatorAct->setVisible(hasMyChild);
// 文档打开且有内容被选中的情况
bool hasSelection =
(activeMyChild() && activeMyChild()->textCursor().hasSelection());
cutAct->setEnabled(hasSelection);
copyAct->setEnabled(hasSelection);
boldAct->setEnabled(hasSelection);
italicAct->setEnabled(hasSelection);
underlineAct->setEnabled(hasSelection);
leftAlignAct->setEnabled(hasSelection);
centerAct->setEnabled(hasSelection);
rightAlignAct->setEnabled(hasSelection);
justifyAct->setEnabled(hasSelection);
colorAct->setEnabled(hasSelection);
}
void MyWord::updateWindowMenu() // 更新窗口菜单
{
windowMenu->clear();
windowMenu->addAction(closeAct);
windowMenu->addAction(closeAllAct);
windowMenu->addSeparator();
windowMenu->addAction(tileAct);
windowMenu->addAction(cascadeAct);
windowMenu->addSeparator();
windowMenu->addAction(nextAct);
windowMenu->addAction(previousAct);
windowMenu->addAction(separatorAct);
QList<QMdiSubWindow *> windows = mdiArea->subWindowList();
separatorAct->setVisible(!windows.isEmpty());
// 显示当前打开着的文档子窗口项
for (int i = 0; i < windows.size(); ++i) {
MyChild *child = qobject_cast<MyChild *>(windows.at(i)->widget());
QString text;
if (i < 9) {
text = tr("&%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
} else {
text = tr("%1 %2").arg(i + 1).arg(child->userFriendlyCurrentFile());
}
QAction *action = windowMenu->addAction(text);
action->setCheckable(true);
action->setChecked(child == activeMyChild());
connect(action, SIGNAL(triggered()), windowMapper, SLOT(map()));
windowMapper->setMapping(action, windows.at(i));
}
enabledText(); // 使得字体设置菜单可用
}
MyChild *MyWord::createMyChild() // 创建子窗口对象
{
MyChild *child = new MyChild;
mdiArea->addSubWindow(child);
connect(child, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool)));
connect(child, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool)));
return child;
}
void MyWord::createActions() // 创建菜单操作(动作)
{
// 【文件】菜单动作
// 新建
newAct = new QAction(QIcon(srcpaths + "/filenew.png"), tr("新建(&N)"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setToolTip("新建"); // 设置工具栏按钮的提示文本信息
newAct->setStatusTip(tr("创建一个新的word文档.")); // 设置状态栏提示文本信息
connect(newAct, SIGNAL(triggered()), this, SLOT(fileNew()));
// 打开
openAct =
new QAction(QIcon(srcpaths + "/fileopen.png"), tr("打开(&O)"), this);
openAct->setShortcuts(QKeySequence::Open);
openAct->setToolTip("打开"); // 设置工具栏按钮的提示文本信息
openAct->setStatusTip(
tr("打开已经存在的word文档.")); // 设置状态栏提示文本信息
connect(openAct, SIGNAL(triggered()), this, SLOT(fileOpen()));
// 保存
saveAct =
new QAction(QIcon(srcpaths + "/filesave.png"), tr("保存(&S)"), this);
saveAct->setShortcuts(QKeySequence::Save);
saveAct->setToolTip("保存");
saveAct->setStatusTip(tr("将当前word文档存盘."));
connect(saveAct, SIGNAL(triggered()), this, SLOT(fileSave()));
// 另存为
saveAsAct = new QAction(tr("另存为(&A)..."), this);
saveAsAct->setShortcuts(QKeySequence::SaveAs);
// saveAsAct->setToolTip("另存为"); // 此工具栏按钮大家根据自已需要决定是保留
saveAsAct->setStatusTip(tr("用另一个新的文件名称,保存当前word文档."));
connect(saveAsAct, SIGNAL(triggered()), this, SLOT(fileSaveAs()));
// 打印
printAct =
new QAction(QIcon(srcpaths + "/fileprint.png"), tr("打印(&P)"), this);
printAct->setShortcuts(QKeySequence::Print);
printAct->setToolTip("打印");
printAct->setStatusTip(tr("打印当前word文档."));
connect(printAct, SIGNAL(triggered()), this, SLOT(filePrint()));
// 打印预览
printPreviewAct = new QAction(tr("打印预览..."), this);
printPreviewAct->setStatusTip(tr("打印预览当前word文档效果."));
connect(printPreviewAct, SIGNAL(triggered()), this, SLOT(filePrintPreview()));
// 退出操作
exitAct = new QAction(tr("退出(X)"), this);
exitAct->setShortcuts(QKeySequence::Quit);
exitAct->setStatusTip("退出Word文件应用程序");
connect(exitAct, SIGNAL(triggered()), qApp, SLOT(closeAllWindows()));
// 【编辑】菜单动作
// 撤销
undoAct =
new QAction(QIcon(srcpaths + "/editundo.png"), tr("撤销(&U)"), this);
undoAct->setShortcuts(QKeySequence::Undo);
undoAct->setToolTip("撤销");
undoAct->setStatusTip(tr("撤销当前Word文档操作."));
connect(undoAct, SIGNAL(triggered()), this, SLOT(undo()));
// 重做
redoAct =
new QAction(QIcon(srcpaths + "/editredo.png"), tr("重做(&R)"), this);
redoAct->setShortcuts(QKeySequence::Redo);
redoAct->setToolTip("重做");
redoAct->setStatusTip(tr("恢复之前的Word文档操作."));
connect(redoAct, SIGNAL(triggered()), this, SLOT(redo()));
// 剪切
cutAct = new QAction(QIcon(srcpaths + "/editcut.png"), tr("剪切(&T)"), this);
cutAct->setShortcuts(QKeySequence::Cut);
cutAct->setToolTip("剪切");
cutAct->setStatusTip(tr("从Word文档中裁剪所选择的倥,将它存放到剪贴板."));
connect(cutAct, SIGNAL(triggered()), this, SLOT(cut()));
// 复制
copyAct =
new QAction(QIcon(srcpaths + "/editcopy.png"), tr("复制(&C)"), this);
copyAct->setShortcuts(QKeySequence::Copy);
copyAct->setToolTip("复制");
copyAct->setStatusTip(tr("复制当前所选中的内容,将它存放到剪贴板."));
connect(copyAct, SIGNAL(triggered()), this, SLOT(copy()));
// 粘贴
pasteAct =
new QAction(QIcon(srcpaths + "/editpaste.png"), tr("粘贴(&P)"), this);
pasteAct->setShortcuts(QKeySequence::Paste);
pasteAct->setToolTip("粘贴");
pasteAct->setStatusTip(tr("将剪贴板里面的内容粘贴到当前word文档."));
connect(pasteAct, SIGNAL(triggered()), this, SLOT(paste()));
// 【格式】-->(字体)菜单动作
// 加粗
boldAct =
new QAction(QIcon(srcpaths + "/textbold.png"), tr("加粗(&B)"), this);
boldAct->setCheckable(true);
boldAct->setShortcut(Qt::CTRL + Qt::Key_B);
boldAct->setToolTip("加粗");
boldAct->setStatusTip(tr("将你所选择的文字进行加粗处理."));
QFont bold;
bold.setBold(true);
boldAct->setFont(bold);
connect(boldAct, SIGNAL(triggered()), this, SLOT(textBold()));
// 倾斜
italicAct =
new QAction(QIcon(srcpaths + "/textitalic.png"), tr("倾斜(&I)"), this);
italicAct->setCheckable(true);
italicAct->setShortcut(Qt::CTRL + Qt::Key_I);
italicAct->setToolTip("倾斜");
italicAct->setStatusTip(tr("将你所选择的文字进行倾斜处理."));
QFont italic;
italic.setItalic(true);
italicAct->setFont(italic);
connect(italicAct, SIGNAL(triggered()), this, SLOT(textItalic()));
// 下划线
underlineAct =
new QAction(QIcon(srcpaths + "/textunder.png"), tr("下划线(&U)"), this);
underlineAct->setCheckable(true);
underlineAct->setShortcut(Qt::CTRL + Qt::Key_U);
underlineAct->setToolTip("下划线");
underlineAct->setStatusTip(tr("将你所选择的文字添加下划线."));
QFont underline;
underline.setUnderline(true);
underlineAct->setFont(underline);
connect(underlineAct, SIGNAL(triggered()), this, SLOT(textUnderline()));
// 【格式】-->(段落)子菜单下各项为同一个菜单项组,我们只能选中其中的一项
QActionGroup *grp = new QActionGroup(this);
connect(grp, SIGNAL(triggered(QAction *)), this, SLOT(textAlign(QAction *)));
if (QApplication::isLeftToRight()) {
leftAlignAct =
new QAction(QIcon(srcpaths + "/textleft.png"), tr("左对齐(&L)"), grp);
centerAct = new QAction(QIcon(srcpaths + "/textcenter.png"),
tr("居中对齐(&E)"), grp);
rightAlignAct =
new QAction(QIcon(srcpaths + "/textright.png"), tr("右对齐(&R)"), grp);
} else {
rightAlignAct =
new QAction(QIcon(srcpaths + "/textright.png"), tr("右对齐(&R)"), grp);
centerAct = new QAction(QIcon(srcpaths + "/textcenter.png"),
tr("居中对齐(&E)"), grp);
leftAlignAct =
new QAction(QIcon(srcpaths + "/textleft.png"), tr("左对齐(&L)"), grp);
}
justifyAct = new QAction(QIcon(srcpaths + "/textjustify.png"),
tr("两端对齐(&J)"), grp);
leftAlignAct->setShortcut(Qt::CTRL + Qt::Key_L);
leftAlignAct->setCheckable(true);
leftAlignAct->setToolTip("左对齐");
leftAlignAct->setStatusTip(tr("将选择文字进行左对齐."));
centerAct->setShortcut(Qt::CTRL + Qt::Key_E);
centerAct->setCheckable(true);
centerAct->setToolTip("居中对齐");
centerAct->setStatusTip(tr("将选择文字进行居中对齐."));
rightAlignAct->setShortcut(Qt::CTRL + Qt::Key_R);
rightAlignAct->setCheckable(true);
rightAlignAct->setToolTip("右对齐");
rightAlignAct->setStatusTip(tr("将选择文字进行右对齐."));
justifyAct->setShortcut(Qt::CTRL + Qt::Key_J);
justifyAct->setCheckable(true);
justifyAct->setToolTip("两端对齐");
justifyAct->setStatusTip(tr("将选择文字进行两端对齐."));
QPixmap pix(16, 16);
pix.fill(Qt::red);
colorAct = new QAction(pix, tr("颜色(&C)..."), this);
colorAct->setToolTip("颜色");
colorAct->setStatusTip(tr("将你选择的文字,设置对应的颜色."));
connect(colorAct, SIGNAL(triggered()), this, SLOT(textColor()));
// 【窗口】菜单
closeAct = new QAction(tr("关闭(&O)"), this);
closeAct->setStatusTip(tr("关闭活动word文档子窗口."));
connect(closeAct, SIGNAL(triggered()), mdiArea, SLOT(closeActiveSubWindow()));
closeAllAct = new QAction(tr("关闭所有(&A)"), this);
closeAllAct->setStatusTip(tr("关闭活动word文档所有子窗口."));
connect(closeAllAct, SIGNAL(triggered()), mdiArea,
SLOT(closeAllSubWindows()));
tileAct = new QAction(tr("平铺(&T)"), this);
tileAct->setStatusTip(tr("平铺子窗口."));
connect(tileAct, SIGNAL(triggered()), mdiArea, SLOT(tileSubWindows()));
cascadeAct = new QAction(tr("层叠(&C)"), this);
cascadeAct->setStatusTip(tr("层叠子窗口."));
connect(cascadeAct, SIGNAL(triggered()), mdiArea, SLOT(cascadeSubWindows()));
nextAct = new QAction(tr("下一个(&T)"), this);
nextAct->setShortcuts(QKeySequence::NextChild);
nextAct->setStatusTip(tr("移动焦点到下一个子窗口."));
connect(nextAct, SIGNAL(triggered()), mdiArea, SLOT(activateNextSubWindow()));
previousAct = new QAction(tr("前一个(&V)"), this);
previousAct->setShortcuts(QKeySequence::PreviousChild);
previousAct->setStatusTip(tr("移动焦点到前一个子窗口."));
connect(previousAct, SIGNAL(triggered()), mdiArea,
SLOT(activatePreviousSubWindow()));
separatorAct = new QAction(this);
separatorAct->setSeparator(true);
// 【帮助】菜单
aboutAct = new QAction(tr("关于(&A)"), this);
aboutAct->setStatusTip("关于Office Word相关信息.");
connect(aboutAct, SIGNAL(triggered()), this, SLOT(about()));
aboutQtAct = new QAction(tr("关于 Qt(&Qt)"), this);
aboutQtAct->setStatusTip("关于 Qt库相关信息.");
connect(aboutQtAct, SIGNAL(triggered()), this, SLOT(aboutQt()));
}
void MyWord::createMenus() // 创建菜单
{
// 【文件】主菜单
fileMenu = menuBar()->addMenu(tr("文件(&F)"));
fileMenu->addAction(newAct);
fileMenu->addAction(openAct);
fileMenu->addSeparator(); // 分隔线
fileMenu->addAction(saveAct);
fileMenu->addAction(saveAsAct);
fileMenu->addSeparator(); // 分隔线
fileMenu->addAction(printAct);
fileMenu->addAction(printPreviewAct);
fileMenu->addSeparator(); // 分隔线
fileMenu->addAction(exitAct);
// 【编辑】主菜单
editMenu = menuBar()->addMenu(tr("编辑(&E)"));
editMenu->addAction(undoAct);
editMenu->addAction(redoAct);
editMenu->addSeparator(); // 分隔线
editMenu->addAction(cutAct);
editMenu->addAction(copyAct);
editMenu->addAction(pasteAct);
// 【格式】主菜单
formatMenu = menuBar()->addMenu(tr("格式(&O)"));
fontMenu = formatMenu->addMenu(tr("字体(&D)")); // 【字体】子菜单
fontMenu->addAction(boldAct);
fontMenu->addAction(italicAct);
fontMenu->addAction(underlineAct);
alignMenu = formatMenu->addMenu(tr("段落")); // 【段落】子菜单
alignMenu->addAction(leftAlignAct);
alignMenu->addAction(centerAct);
alignMenu->addAction(rightAlignAct);
alignMenu->addAction(justifyAct);
formatMenu->addAction(colorAct);
// 【窗口】主菜单
windowMenu = menuBar()->addMenu(tr("窗口(&W)"));
updateWindowMenu();
connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowMenu()));
menuBar()->addSeparator();
// 【帮助】主菜单
helpMenu = menuBar()->addMenu(tr("帮助(&H)"));
helpMenu->addAction(aboutAct);
helpMenu->addSeparator(); // 分隔线
helpMenu->addAction(aboutQtAct);
}
void MyWord::createToolBars() // 创建工具条
{
//"文件"工具栏
fileToolBar = addToolBar(tr("文件"));
fileToolBar->addAction(newAct);
fileToolBar->addAction(openAct);
fileToolBar->addAction(saveAct);
fileToolBar->addSeparator(); // 分隔条
fileToolBar->addAction(printAct);
//"编辑"工具栏
editToolBar = addToolBar(tr("编辑"));
editToolBar->addAction(undoAct);
editToolBar->addAction(redoAct);
editToolBar->addSeparator(); // 分隔条
editToolBar->addAction(cutAct);
editToolBar->addAction(copyAct);
editToolBar->addAction(pasteAct);
//"格式"工具栏
formatToolBar = addToolBar(tr("格式"));
formatToolBar->addAction(boldAct);
formatToolBar->addAction(italicAct);
formatToolBar->addAction(underlineAct);
formatToolBar->addSeparator(); // 分隔条
formatToolBar->addAction(leftAlignAct);
formatToolBar->addAction(centerAct);
formatToolBar->addAction(rightAlignAct);
formatToolBar->addAction(justifyAct);
formatToolBar->addSeparator(); // 分隔条
formatToolBar->addAction(colorAct);
// 组合工具栏
addToolBarBreak(Qt::TopToolBarArea); // 使这个工具条在界面上另起一行显示
comboToolBar = addToolBar(tr("组合选择"));
comboStyle = new QComboBox();
comboToolBar->addWidget(comboStyle);
comboStyle->addItem("标准");
comboStyle->addItem("项目符号 (●)");
comboStyle->addItem("项目符号 (○)");
comboStyle->addItem("项目符号 (■)");
comboStyle->addItem("编号 (⒈⒉⒊)");
comboStyle->addItem("编号 ( a.b.c.)");
comboStyle->addItem("编号 ( A.B.C.)");
comboStyle->addItem("编号 (ⅰ.ⅱ.ⅲ.)");
comboStyle->addItem("编号 (Ⅰ.Ⅱ.Ⅲ.)");
comboStyle->setStatusTip("段落加标号或编号");
connect(comboStyle, SIGNAL(activated(int)), this, SLOT(textStyle(int)));
comboFont = new QFontComboBox();
comboToolBar->addWidget(comboFont);
comboFont->setStatusTip("更改字体");
connect(comboFont, SIGNAL(activated(QString)), this,
SLOT(textFamily(QString)));
comboSize = new QComboBox();
comboToolBar->addWidget(comboSize);
comboSize->setEditable(true);
comboSize->setStatusTip("更改字号");
QFontDatabase db;
foreach (int size, db.standardSizes())
comboSize->addItem(QString::number(size));
connect(comboSize, SIGNAL(activated(QString)), this, SLOT(textSize(QString)));
comboSize->setCurrentIndex(
comboSize->findText(QString::number(QApplication::font().pointSize())));
}
void MyWord::createStatusBar() // 创建状态条
{
statusBar()->showMessage(tr("就绪状态"));
}
MyChild *MyWord::activeMyChild() // 获取激活的子窗口
{
if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
return qobject_cast<MyChild *>(activeSubWindow->widget());
return 0;
}
// 打开文件用
QMdiSubWindow *MyWord::findMyChild(const QString &fileName) // 查找子窗口
{
QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
MyChild *myChild = qobject_cast<MyChild *>(window->widget());
if (myChild->currentFile() == canonicalFilePath)
return window;
}
return 0;
}
void MyWord::setActiveSubWindow(QWidget *window) // 设置active激活窗口
{
if (!window)
return;
mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow *>(window));
}
void MyWord::fileNew() // 新建
{
MyChild *child = createMyChild();
child->newFile();
child->show();
enabledText(); // 使得字体设置菜单可用的
}
void MyWord::fileOpen() // 打开
{
QString filename = QFileDialog::getOpenFileName(
this, tr("打开"), QString(),
tr("HTML 文档(*.htm *.html);;所有文件(*.*)"));
if (!filename.isEmpty()) {
QMdiSubWindow *existing = findMyChild(filename);
if (existing) {
mdiArea->setActiveSubWindow(existing);
return;
}
MyChild *child = createMyChild();
if (child->loadFile(filename)) {
statusBar()->showMessage(tr("文件已经加载"), 2000);
child->show();
enabledText();
} else {
child->close();
}
}
}
void MyWord::fileSave() // 保存
{
if (activeMyChild() && activeMyChild()->save())
statusBar()->showMessage(tr("Word文档保存成功."), 2000);
}
void MyWord::fileSaveAs() // 另存为
{
if (activeMyChild() && activeMyChild()->saveAs())
statusBar()->showMessage(tr("Word文档另存为成功."));
}
void MyWord::undo() // 撤消
{
if (activeMyChild())
activeMyChild()->undo();
}
void MyWord::redo() // 重做
{
if (activeMyChild())
activeMyChild()->redo();
}
void MyWord::cut() // 剪切
{
if (activeMyChild())
activeMyChild()->cut();
}
void MyWord::copy() // 复制
{
if (activeMyChild())
activeMyChild()->copy();
}
void MyWord::paste() // 粘贴
{
if (activeMyChild())
activeMyChild()->paste();
}
void MyWord::enabledText() // 使得【格式】下的各个子菜单项可用
{
boldAct->setEnabled(true);
italicAct->setEnabled(true);
underlineAct->setEnabled(true);
leftAlignAct->setEnabled(true);
centerAct->setEnabled(true);
rightAlignAct->setEnabled(true);
justifyAct->setEnabled(true);
colorAct->setEnabled(true);
}
void MyWord::textBold() // 加粗
{
QTextCharFormat fmt;
fmt.setFontWeight(boldAct->isChecked() ? QFont::Bold : QFont::Normal);
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
void MyWord::textItalic() // 斜体
{
QTextCharFormat fmt;
fmt.setFontItalic(italicAct->isChecked());
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
void MyWord::textUnderline() // 下划线
{
QTextCharFormat fmt;
fmt.setFontUnderline(underlineAct->isChecked());
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
void MyWord::textAlign(QAction *a) // 文本对齐判断函数
{
if (activeMyChild()) {
if (a == leftAlignAct)
activeMyChild()->setAlign(1);
else if (a == centerAct)
activeMyChild()->setAlign(2);
else if (a == rightAlignAct)
activeMyChild()->setAlign(3);
else if (a == justifyAct)
activeMyChild()->setAlign(4);
}
}
void MyWord::textStyle(int styleIndex) // 文本样式
{
if (activeMyChild())
activeMyChild()->setStyle(styleIndex);
}
void MyWord::textFamily(const QString &f) {
QTextCharFormat fmt;
fmt.setFontFamily(f);
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
void MyWord::textSize(const QString &p) // 文本大小
{
qreal pointsize = p.toFloat();
if (p.toFloat() > 0) {
QTextCharFormat fmt;
fmt.setFontPointSize(pointsize);
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
}
void MyWord::textColor() // 文本颜色
{
if (activeMyChild()) {
QColor color = QColorDialog::getColor(activeMyChild()->textColor(), this);
if (!color.isValid())
return;
QTextCharFormat fmt;
fmt.setForeground(color);
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
colorChanged(color);
}
}
void MyWord::fontChanged(const QFont &f) // 字体更改的UI同步
{
comboFont->setCurrentIndex(comboFont->findText(QFontInfo(f).family()));
comboSize->setCurrentIndex(
comboSize->findText(QString::number(f.pointSize())));
boldAct->setChecked(f.bold());
italicAct->setChecked(f.italic());
underlineAct->setChecked(f.underline());
}
void MyWord::colorChanged(const QColor &c) // 颜色更改的UI同步
{
QPixmap pix(16, 16);
pix.fill(c);
colorAct->setIcon(pix);
}
void MyWord::alignmentChanged(Qt::Alignment a) // 对齐判断的UI同步
{
if (a & Qt::AlignLeft)
leftAlignAct->setChecked(true);
else if (a & Qt::AlignCenter)
centerAct->setChecked(true);
else if (a & Qt::AlignRight)
rightAlignAct->setChecked(true);
else if (a & Qt::AlignJustify)
justifyAct->setChecked(true);
}
void MyWord::filePrint() // 打印文档
{
QPrinter printer(QPrinter::HighResolution);
QPrintDialog *pdlg = new QPrintDialog(&printer, this);
if (activeMyChild()->textCursor().hasSelection())
pdlg->addEnabledOption(QAbstractPrintDialog::PrintSelection);
pdlg->setWhatsThis(tr("打印文档"));
if (pdlg->exec() == QDialog::Accepted)
activeMyChild()->print(&printer);
delete pdlg;
}
void MyWord::filePrintPreview() // 文件打印预览
{
QPrinter printer(QPrinter::HighResolution);
QPrintPreviewDialog preview(&printer, this);
connect(&preview, SIGNAL(paintRequested(QPrinter *)),
SLOT(printPreview(QPrinter *)));
preview.exec();
}
void MyWord::printPreview(QPrinter *printer) // 打印预览
{
activeMyChild()->print(printer);
}
void MyWord::about() // 关于
{
QMessageBox::about(this, tr("关于"),
tr("基于Qt5实现的Word类文字处理软件!!!"));
}
对.h文件中声明的函数进行实现
MyWord::MyWord(QWidget *parent) : QMainWindow(parent) {
setWindowIcon(QIcon(srcpaths + "/wordlogo2.png"));
mdiArea = new QMdiArea;
mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
setCentralWidget(mdiArea);
connect(mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)), this,
SLOT(updateMenus()));
windowMapper = new QSignalMapper(this);
connect(windowMapper, SIGNAL(mapped(QWidget *)), this,
SLOT(setActiveSubWindow(QWidget *)));
createActions(); // 创建菜单操作(动作)
createMenus();
createToolBars();
createStatusBar();
updateMenus();
resize(900, 600);
setWindowTitle(tr("Offce办公自动化文字编辑软件 V2.1"));
setUnifiedTitleAndToolBarOnMac(true);
}
初始化主窗口并做信号槽连接。
窗口外观:setWindowIcon(QIcon(...))设置软件图标;setWindowTitle()指定标题;resize(900,600)设置初始大小。
多文档区域:创建QMdiArea多文档区域(含垂直、水平滚动条Qt::ScrollBarAsNeeded),并用setCentralWidget(mdiArea)作为主窗口中心控件。
信号槽函数connect():subWindowActivated()、updateMenus(),子窗口切换时自动更新菜单。
windowMapper(QSignalMapper)映射子窗口,调用setActiveSubWindow()。
界面部分:调用createActions()、createMenus()、createToolBars()、createStatusBar(),将动作、菜单、工具栏和状态栏搭建完整。
void MyWord::createActions() // 创建菜单操作(动作)
{
// 【文件】菜单动作
// 新建
newAct = new QAction(QIcon(srcpaths + "/filenew.png"), tr("新建(&N)"), this);
newAct->setShortcuts(QKeySequence::New);
newAct->setToolTip("新建"); // 设置工具栏按钮的提示文本信息
newAct->setStatusTip(tr("创建一个新的word文档.")); // 设置状态栏提示文本信息
connect(newAct, SIGNAL(triggered()), this, SLOT(fileNew()));
...
// 【格式】-->(字体)菜单动作
// 加粗
boldAct =
new QAction(QIcon(srcpaths + "/textbold.png"), tr("加粗(&B)"), this);
boldAct->setCheckable(true);
boldAct->setShortcut(Qt::CTRL + Qt::Key_B);
boldAct->setToolTip("加粗");
boldAct->setStatusTip(tr("将你所选择的文字进行加粗处理."));
QFont bold;
bold.setBold(true);
boldAct->setFont(bold);
connect(boldAct, SIGNAL(triggered()), this, SLOT(textBold()));
...
}
createActions()把菜单功能与用户操作(图标、快捷键、提示、槽函数)紧密结合,使得“新建文档”“加粗”等核心操作直观。
【文件】新建动作:
newAct=newQAction(QIcon(srcpaths+"/filenew.png"),tr("新建(&N)"),this);
newAct->setShortcuts(QKeySequence::New);设置图标、快捷键Ctrl+N。
connect(newAct,SIGNAL(triggered()),this,SLOT(fileNew()));绑定槽函数fileNew()
用setToolTip()、setStatusTip()设置工具栏提示和状态栏提示。
【格式】加粗动作:
boldAct=newQAction(QIcon(srcpaths+"/textbold.png"),tr("加粗(&B)"),this);boldAct->setCheckable(true);boldAct->setShortcut(Qt::CTRL+Qt::Key_B);connect(boldAct,SIGNAL(triggered()),this,SLOT(textBold()));
除了与“新建动作”一样的图标、快捷键(Ctrl+B)、提示信息和绑定槽函数外,setCheckable(true)让该动作具备开关状态,并通过setFont()这个动作的文本在菜单栏和工具栏中以粗体显示。
以上述两个动作为例,可得动作创建标准流程:QAction=图标+名称(+快捷键)+提示信息+槽函数(+其它)
void MyWord::createMenus() // 创建菜单
{
...
// 【格式】主菜单
formatMenu = menuBar()->addMenu(tr("格式(&O)"));
fontMenu = formatMenu->addMenu(tr("字体(&D)")); // 【字体】子菜单
fontMenu->addAction(boldAct);
fontMenu->addAction(italicAct);
fontMenu->addAction(underlineAct);
alignMenu = formatMenu->addMenu(tr("段落")); // 【段落】子菜单
alignMenu->addAction(leftAlignAct);
alignMenu->addAction(centerAct);
alignMenu->addAction(rightAlignAct);
alignMenu->addAction(justifyAct);
formatMenu->addAction(colorAct);
...
}
以【格式】菜单为例,通过formatMenu=menuBar()->addMenu(tr("格式(&O)"));创建主菜单。然后用formatMenu->addMenu进一步添加为字体、段落两个子菜单,用formatMenu->addAction添加一个独立动作"颜色"。接着在字体子菜单(fontMenu)、段落子菜单(alignMenu)分别添加相应动作。
MyChild *MyWord::activeMyChild() // 获取激活的子窗口
{
if (QMdiSubWindow *activeSubWindow = mdiArea->activeSubWindow())
return qobject_cast<MyChild *>(activeSubWindow->widget());
return 0;
}
在多文档界面(MDI)中获取当前激活的子窗口。
通过mdiArea->activeSubWindow()判断是否有活动的子窗口;若存在,用qobject_cast<MyChild*>将子窗口中的QWidget类型转换成MyChild类型并返回;若没有活动窗口,则返回0(空指针)。
void MyWord::updateMenus() // 更新菜单
{
// 至少有一个子文档打开着的情况
bool hasMyChild = activeMyChild();
saveAct->setEnabled(hasMyChild);
...
// 文档打开且有内容被选中的情况
bool hasSelection =
(activeMyChild() && activeMyChild()->textCursor().hasSelection());
cutAct->setEnabled(hasSelection);
...
}
根据子文档的打开情况以及文档内容是否被选中,设置各个动作的启用情况。
MyChild *MyWord::createMyChild() // 创建子窗口对象
{
MyChild *child = new MyChild;
mdiArea->addSubWindow(child);
connect(child, SIGNAL(copyAvailable(bool)), cutAct, SLOT(setEnabled(bool)));
connect(child, SIGNAL(copyAvailable(bool)), copyAct, SLOT(setEnabled(bool)));
return child;
}
创建并管理一个子文档窗口。
newMyChild新建一个子窗口对象。通过mdiArea->addSubWindow(child)把它添加到多文档区域中。利用信号槽机制,将子窗口的copyAvailable(bool)信号连接到cutAct和copyAct的setEnabled(bool)槽上。当子窗口中有可复制的内容时,剪切和复制动作(按钮)会自动启用,否则保持灰色不可用状态。最后返回新建的子窗口对象。
void MyWord::textBold() // 加粗
{
QTextCharFormat fmt;
fmt.setFontWeight(boldAct->isChecked() ? QFont::Bold : QFont::Normal);
if (activeMyChild())
activeMyChild()->mergeFormatOnWordOrSelection(fmt);
}
文本格式化函数textBold()、textItalic()、textFamily()等,逻辑类似,以textBold()举例。
创建QTextCharFormatfmt来定义字符格式。通过boldAct->isChecked()判断当前“加粗”按钮是否被选中:若选中则设置为QFont::Bold,否则为QFont::Normal。最后调用activeMyChild()->mergeFormatOnWordOrSelection(fmt),将该格式应用到当前激活的子窗口上。
QMdiSubWindow *MyWord::findMyChild(const QString &fileName) // 查找子窗口
{
QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
foreach (QMdiSubWindow *window, mdiArea->subWindowList()) {
MyChild *myChild = qobject_cast<MyChild *>(window->widget());
if (myChild->currentFile() == canonicalFilePath)
return window;
}
return 0;
}
用于在多文档区域中查找指定文件对应的子窗口,避免同一个文件被重复打开。 QFileInfo(fileName).canonicalFilePath()获取文件的标准路径,再通过mdiArea->subWindowList()遍历所有子窗口,逐个取出其中的MyChild对象。比较myChild->currentFile()与标准路径是否一致,如果匹配,就返回该子窗口指针。如果没有找到,则返回0。
void MyWord::fileOpen() // 打开
{
QString filename = QFileDialog::getOpenFileName(
this, tr("打开"), QString(),
tr("HTML 文档(*.htm *.html);;所有文件(*.*)"));
if (!filename.isEmpty()) {
QMdiSubWindow *existing = findMyChild(filename);
if (existing) {
mdiArea->setActiveSubWindow(existing);
return;
}
MyChild *child = createMyChild();
if (child->loadFile(filename)) {
statusBar()->showMessage(tr("文件已经加载"), 2000);
child->show();
enabledText();
} else {
child->close();
}
}
}
通过QFileDialog::getOpenFileName()弹出对话框,让用户选择文件(默认支持HTML文间或所有文件)。若文件名不为空,调用findMyChild(filename)检查该文件是否已在子窗口中打开:如果已存在,直接mdiArea->setActiveSubWindow(existing)激活该窗口,避免重复打开;如果不存在,通过createMyChild()新建子窗口,并调用child->loadFile(filename)加载文件。若加载成功,状态栏输出statusBar()->showMessage("文件已经加载"),并展示子窗口child->show(),启用格式菜单enabledText();加载失败,则关闭子窗口child->close()。
void MyWord::fontChanged(const QFont &f) // 字体更改
{
comboFont->setCurrentIndex(comboFont->findText(QFontInfo(f).family()));
comboSize->setCurrentIndex(
comboSize->findText(QString::number(f.pointSize())));
boldAct->setChecked(f.bold());
italicAct->setChecked(f.italic());
underlineAct->setChecked(f.underline());
}
状态同步函数fontChanged(),colorChanged()等,逻辑类似,以fontChanged()举例。
在UI界面中同步字体状态,保证菜单/工具栏的状态与实际文字格式保持一致。comboFont->setCurrentIndex(...)设置字体组合框为当前字体。comboSize->setCurrentIndex(...)设置字体大小组合框为当前大小。boldAct->setChecked(f.bold())等动作按钮的勾选状态也是同理。
2288

被折叠的 条评论
为什么被折叠?



