项目地址:GitHub - Outlier9/CatEditor: Cat文本编辑器--Qt
有帮助的话各位点点 star 啦,感谢!
如果有需要学习该项目的人,觉得看文档较为困难,可以加我联系方式,给github点个star后可免费提供学习视频!!!
在.ui
文件的各个Action
选项右击选择转到槽,选择triggered
信号,调用对应的方法(均已封装好,可以直接调用)
这里有新建、关闭、关闭所有、平铺、层叠、下一个、上一个
void MainWindow::on_newAction_triggered() { docNew(); } void MainWindow::on_closeAction_triggered() { ui->mdiArea->closeActiveSubWindow(); } void MainWindow::on_closeAllAction_triggered() { ui->mdiArea->closeAllSubWindows(); } void MainWindow::on_tileAction_triggered() { ui->mdiArea->tileSubWindows(); } void MainWindow::on_cascadeAction_triggered() { ui->mdiArea->cascadeSubWindows(); } void MainWindow::on_nextAction_triggered() { ui->mdiArea->activateNextSubWindow(); } void MainWindow::on_previousAction_triggered() { ui->mdiArea->activatePreviousSubWindow(); }
但是在关闭窗口的时候,需要处理窗口关闭前的清理工作或处理用户的关闭请求,以防数据丢失,判断是否有未保存或正在进行中的窗口,所以在头文件中声明事件处理函数
void MainWindow::closeEvent(QCloseEvent *event) { ui->mdiArea->closeAllSubWindows(); //防止在关闭主窗口时意外丢失未保存的数据 if(ui->mdiArea->currentSubWindow()) event->ignore(); //如果存在当前活动的子窗口,忽略关闭事件。这意味着窗口不会关闭,用户可以继续操作 else event->accept(); //接受此事件 }
在.ui
界面对openAction转到槽,选triggered信号,然后和新建文档一样,我们将打开操作进行封装,命名为docOpen
加载文件的时候有可能成功也可能失败,所以这里需要一个验证
bool loadDoc(const QString& docName); //加载文档验证 void setCurDoc(const QString& docName);//设置当前文档路径及相关状态
//加载指定名称的文档 bool ChileWnd::loadDoc(const QString &docName) { if(!docName.isEmpty()) { QFile file(docName); if(!file.exists()) //文件是否存在 return false; if(!file.open(QFile::ReadOnly)) // 打开文档并检查是否只读 return false; //如果文件不能以只读方式打开 QByteArray text = file.readAll(); //读取文件内容 if(Qt::mightBeRichText(text)) // 是否是富文本类型并设置文本内容 setHtml(text); else setPlainText(text); //设置为纯文本 setCurDoc(docName); //设置当前文档路径 connect(document(),SIGNAL(contentsChanged()),this,SLOT(docBeModified())); return true; } } //设置当前文档路径及相关状态 void ChileWnd::setCurDoc(const QString &docName) { //返回标准名称路径,可以过滤“.”和“..” m_CurDocPath = QFileInfo(docName).canonicalFilePath(); m_bSaved = true; //文档已被保存 document()->setModified(false); //文档未被改动 setWindowModified(false); //窗口不显示改动标识 setWindowTitle(getCurDocName() + "[*]"); //设置子窗口标题 }
public: void docOpen(); private: //匹配子窗口是否存在 QMdiSubWindow* findChileWnd(const QString &docName);
void MainWindow::docOpen() { QString docName = QFileDialog::getOpenFileName(this, "打开文档", "", "文本文件(*.txt);;" "HTML文件(*.html *.htm);;" "所有文件(*.*)"); //如果用户选择了文件并且文件名不为空,则继续执行 if(!docName.isEmpty()) { //查找是否已经有窗口打开了这个文档。如果找到已经存在的窗口,则将其激活,并返回 QMdiSubWindow *existWnd = findChileWnd(docName); if(existWnd) { ui->mdiArea->setActiveSubWindow(existWnd); return; } //如果文档尚未被打开,创建一个新的 ChileWnd 子窗口,并将其添加到 QMdiArea 中 ChileWnd *childWnd = new ChileWnd; ui->mdiArea->addSubWindow(childWnd); //连接子窗口的信号和主窗口的槽 connect(childWnd,SIGNAL(copyAvailable(bool)), ui->cutAction,SLOT(setEnabled(bool))); connect(childWnd,SIGNAL(copyAvailable(bool)), ui->copyAction,SLOT(setEnabled(bool))); //加载文档内容 if(childWnd->loadDoc(docName)) { //显示状态栏信息 "文档已打开" 持续 3 秒 statusBar()->showMessage("文档已打开",3000); childWnd->show(); formatEnable(); } else { //加载失败,则关闭创建的子窗口 childWnd->close(); } } } //匹配子窗口是否存在 QMdiSubWindow *MainWindow::findChileWnd(const QString &docName) { //获取文件的标准路径 QString strFile = QFileInfo(docName).canonicalFilePath(); //遍历所有子窗口 foreach(QMdiSubWindow* subWnd,ui->mdiArea->subWindowList()){ //转换子窗口的 widget 为 ChileWnd ChileWnd* childWnd = qobject_cast(subWnd->widget()); //比较子窗口的当前文档路径 if(childWnd->m_CurDocPath == strFile) //如果相同,表示找到了对应的子窗口,返回该子窗口指针 subWnd return subWnd; } //未找到对应子窗口的情况 return 0; }
在.ui
界面对saveAction和saveOther转到槽,选triggered信号,然后和新建文档一样,我们将两种保存操作进行封装,命名为docSave
和docSaveAs
bool saveDoc(); //保存文档 bool saveAsDoc(); //另存为 bool saveDocOpt(QString docName); //保存文件的操作逻辑
//保存文档 bool ChileWnd::saveDoc() { //查询文档当前是否保存的状态(全局变量) if(m_bSaved) return saveDocOpt(m_CurDocPath); else saveAsDoc(); } //另存为 bool ChileWnd::saveAsDoc() { QString docName = QFileDialog::getSaveFileName(this, "另存为", "文档.txt", // 默认保存为 txt 格式 "文本文件(*.txt);;" "HTML文件(*.html *.htm);;" "Word文件(*.doc *.docx);;" "PDF文件(*.pdf);;" "图片文件(*.png *.jpg *.jpeg *.bmp);;" "所有文件(*.*)"); if(docName.isEmpty()) return false; else return saveDocOpt(docName); } //保存文件的操作逻辑 bool ChileWnd::saveDocOpt(QString docName) { //CaseInsensitive大小写都可以 if( !(docName.endsWith(".htm",Qt::CaseInsensitive)|| docName.endsWith(".html",Qt::CaseInsensitive))) { //如果没有后缀就添加 docName += ".html"; } 创建一个 QTextDocumentWriter 对象 writer,用于写入文档 QTextDocumentWriter writer(docName); bool isSuccess = writer.write(this->document()); //如果成功写入,就设置文档名字 if(isSuccess) { setCurDoc(docName); } return isSuccess; }
void docSave(); void docSaveAs();
//保存 void MainWindow::docSave() { //如果该活动窗口存在且它的saveDoc返回值为true也就是保存成功 if(activateChildWnd() && activateChildWnd()->saveDoc()) { statusBar()->showMessage("保存成功",3000); } } //另存为 void MainWindow::docSaveAs() { //如果该活动窗口存在且它的saveDoc返回值为true也就是保存成功 if(activateChildWnd() && activateChildWnd()->saveAsDoc()) { statusBar()->showMessage("保存成功",3000); } } //Action转到槽 void MainWindow::on_saveAction_triggered() { docSave(); } void MainWindow::on_saveOther_triggered() { docSaveAs(); }
在关闭文档的时候,如果文档此时的状态是被修改且未保存,就出现一个提示并可以选择保存,在子类中实现该方法promptSave
,然后添加关闭事件处理
protected: void closeEvent(QCloseEvent *event); //关闭事件发生时,进行未保存该文档提示 private: bool promptSave(); //提示未保存但是要关闭的时候进行保存
void ChileWnd::closeEvent(QCloseEvent *event) { if(promptSave()) event->accept(); else event->ignore(); } //提示未保存但是要关闭的时候进行保存 bool ChileWnd::promptSave() { if(!document()->isModified()) return true; //提示消息框 QMessageBox::StandardButton result; result = QMessageBox::warning(this, QString("系统提示"), QString("文档%1已修改,是否保存?") .arg(getCurDocName()), QMessageBox::Yes | QMessageBox::Discard | QMessageBox::Cancel); if(result == QMessageBox::Yes) return saveDoc(); else if(result == QMessageBox::Cancel) return false; return true; if(!document()->isModified()) return true; }
现在点击打开和保存就已经生效了