基于QT(C++)实现(图形界面)文档编辑器
文档编辑器
主要内容
此次课程设计较规范地实现了一个多文档编辑器
类似于记事本,可以实时输入文本。支持 txt 文件的读取、保存、撤销等工作等。
一、设计思路
界面设计 先进行主窗口菜单栏的设计。
设计完菜单栏,向 Action Editor 添加子菜单。
1.1 子窗口设计
子窗口中心部件使用 QTextEdit 类,故实现一个继承于 QTextEdit 的类,类函数由思维导图提供
如下:
1.2 实现菜单的功能
1.2.1 更新菜单的状态
通过窗口的状态确定,哪些功能键可以使用,哪些不能。
#include <QMainWindow>
//增加类MdiChild的前置声明
class MdiChild;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{Q_OBJECT
public:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();
private slots:void on_actionNew_triggered();void updateMenus(); //更新菜单
private:Ui::MainWindow *ui;QAction *actionSeparator; //分隔符MdiChild *activeMdiChild(); //活动窗口
};
1.2.2 实现新建文件的操作
MdiChild * MainWindow::createMdiChild() //创建子窗口部件
{MdiChild *child = new MdiChild; //创建MdiChild部件ui->mdiArea->addSubWindow(child); //向多文档区域添加子窗口,child为中心部件connect(child,SIGNAL(copyAvailable(bool)),ui->actionCut,SLOT(setEnabled(bool)));// 根据QTextEdit类的是否可以复制信号设置剪切复制动作是否可用connect(child,SIGNAL(copyAvailable(bool)),ui->actionCopy,SLOT(setEnabled(bool)));// 根据QTextDocument类的是否可以撤销恢复信号设置撤销恢复动作是否可用connect(child->document(),SIGNAL(undoAvailable(bool)),ui->actionUndo,SLOT(setEnabled(bool)));connect(child->document(),SIGNAL(redoAvailable(bool)),ui->actionRedo,SLOT(setEnabled(bool)));return child;
}
1.2.3 实现文件打开操作
void MainWindow::on_actionOpen_triggered() // 打开文件菜单
{QString filePath =QFileDialog::getOpenFileName(this); // 获取文件路径if (!filePath.isEmpty()){// 如果路径不为空,则查看该文件是否已经打开QMdiSubWindow *existing =findMdiChild(filePath);if (existing){// 如果已经存在,则将对应的子窗口设置为活动窗口ui->mdiArea->setActiveSubWindow(existing);return;}MdiChild *child = createMdiChild(); // 如果没有打开,则新建子窗口if (child->loadFile(filePath)){ui->statusBar->showMessage(tr("打开文件成功"), 2000);child->show();}else{child->close();}}
}
1.2.4 添加子窗口列表
void MainWindow::updateWindowMenu() // 更新窗口菜单
{ui->menuW->clear(); // 先清空菜单,然后再添加各个菜单动作ui->menuW->addAction(ui->actionClose);ui->menuW->addAction(ui->actionCloseAll);ui->menuW->addSeparator();ui->menuW->addAction(ui->actionTile);ui->menuW->addAction(ui->actionCascade);ui->menuW->addSeparator();ui->menuW->addAction(ui->actionNext);ui->menuW->addAction(ui->actionPrevious);ui->menuW->addAction(actionSeparator);QList<QMdiSubWindow *> windows = ui->mdiArea->subWindowList();actionSeparator->setVisible(!windows.isEmpty());// 如果有活动窗口,则显示间隔器for (int i = 0; i < windows.size(); ++i){// 遍历各个子窗口MdiChild *child = qobject_cast<MdiChild *>(windows.at(i)->widget());QString text;if (i < 9){// 如果窗口数小于9,则设置编号为快捷键text = tr("&%1 %2").arg(i + 1).arg(child->getFileNameFromPath());}else{text = tr("%1 %2").arg(i + 1).arg(child->getFileNameFromPath());}QAction *action = ui->menuW->addAction(text); // 添加动作到菜单action->setCheckable(true); // 设置动作可以选择// 设置当前活动窗口动作为选中状态action ->setChecked(child ==activeMdiChild());// 关联动作的触发信号到信号映射器的map()槽函数上,这个函数会发射mapped()信号connect(action, SIGNAL(triggered()),windowMapper, SLOT(map()));
// 将动作与相应的窗口部件进行映射,在发射mapped()信号时就会以这个窗口部件为参数windowMapper->setMapping(action,windows.at(i));}
}
1.2.5 其它功能
因为在前面已经把核心的功能都实现了,而且像剪切、复制、撤销等常用功能,QTextEdit 类已经提供了,所以这里只需要调用相应的函数即可。
二、遇到的问题与解决方案
2.1 文件名问题
实现文档编辑器,需要在未保存的文件名上加上(*), 这里使用了一个 document()的库函数
根据文档的 isModified()函数的返回值,判断我们编辑器内容是否被更改了。如果被更改了,参数为 true,则 setWindowModified()就会在设置了[ ]号的地方显示“”号 setWindowModified(document()->isModified()); setWindowModified()为库函数
2.2 添加子窗口列表
我们想每添加一个子窗口就可以在窗口菜单中罗列出它的文件名,而且可以在这个列表中选择一个子窗口,将它设置为活动窗口。这个看似很好实现,只要为窗口菜单添加菜单动作,然后关联这个动作的触发信号到设置活动窗口槽上就可以了。但是,如果有很多个子窗口怎么办,难道要一个一个进行关联吗,那怎么获知是哪个动作?这里使用了一个信号映射器 QSignalMapper 类,它可以实现对多个相同部件的相同信号进行映射,为其添加字符串或者数值参数,然后再发射出去。
这里遍历了多文档区域的各个子窗口,然后以它们中的文件名为文本创建了动作,并将这些动作添加到窗口菜单中。我们将动作的触发信号关联到信号映射器的 map()槽上,然后设置了动作与其对应的子窗口之间的映射,这样触发菜单时就会执行 map()函数,而它又会发射 mapped() 信号,这个 mapped() 函数会以子窗口部件为参数,因为在构造函数中设置了这个信号与setActiveSubWindow()函数的关联,所以最终会执行设置活动子窗口函数,并且设置选择的动作指定的子窗口为活动窗口