《一》Word文字編輯軟件---架構設計分析

1,簡單介紹

?????? 今天,我們來模擬offic軟件中的word文檔,運行如圖:

運行程序后會出現主界面,頂端的菜單欄包括“文件”“編輯”“格式”“窗口”和“幫助五個主菜單。
菜單欄下面是工具欄,包含了系統常用的功能按鈕。工具欄有四個工具條,分別將一組相關功能按鈕或控件組織在一起 。

工具欄的第一行有三個工具條: 第一個工具條包括新建、打開、保存、打印等文檔管理功能,
第二個工具條包括撤銷、重做、剪切、復制和粘貼這些最基本的文本編輯功能,
第三個工具條是各種較高級的文字字體格式設置按鈕,包括加粗、傾斜、加下畫線,還包括段落對齊及文本顏色設置。

在工具欄的第二行的工具條中有三個組合選擇框控件,用于為文檔添加段落標號和編號,以及選擇特殊字體和更改字號。利用該工具條可以完成更復雜的文檔排版和字體美化工作。
此外,在圖中還給出了使用該軟件制作出的二個文檔示例。用Qt版MyWord字處理軟件制作出的文檔統一以HTML格式存盤,可使用Web瀏覽器打開觀看效果。

開發這個軟件主要分為如下三個階段進行。

(1) 界面設計開發
界面設計開發內容包括菜單系統設計、工具欄設計、多窗體MDI程序框架的建立及多個文檔子窗口的管理和控制等。
(2) 文本編輯功能實現
文本編輯功能實現主要包括文檔的建立、打開和保存,文本的剪切、復制和粘貼,操作撤銷與恢復等這些最基本的文檔編輯功能。
(3) 排版美化功能實現
排版美化功能實現包括字體選擇,字形、字號和文字顏色的設置,文檔段落標號和編號的添加,段落對齊方式設置等高級功能實現。

但是今天,我們首先從架構設計分析,先完成他的文檔建立,保存等功能,在一步步去實現其他:

2,創建文件

首先我們新建文件,選擇:

名字按自己想法來取,后面這里我們先不要ui文件,選擇mainwindow:

后面就創建好了

對于各種新建,保存,另存為等功能,我們需要新建一個C++類。

類的名字自己取,基類選擇QTextEdit,因為:QTextEdit是Qt中提供的一個用于文本編輯的控件,支持對富文本進行編輯和格式化,可以用于各種應用程序中,如文本編輯器、筆記應用、電子郵件客戶端等。

這樣我們就創建好了一個mychild的源文件和頭文件。

我們說過,我們建立這個類要完成的就是新建,保存等操作,所以我們要定義一些函數和槽函數來實現:

#ifndef MYCHILD_H
#define MYCHILD_H#include<QTextEdit>class MyChild : public QTextEdit
{Q_OBJECT
public:MyChild();void NewFile();//新建文件bool LoadFile(const QString &filename);//導入文件bool Save();//保存bool SaveAs();//另存為bool SaveFile(QString filename);//保存文件QString userFriendlyCurrentFile();//用戶友好型當前文件QString currentFile(){return curFile;};//當前文件void MergeFormationOnWordOrSelection(const QTextCharFormat &format);//格式字體void SetAlign(int align);//對齊void SetStyle(int style);//段落標號,編號等風格protected:void closeEvent(QCloseEvent *event);// 可以通過參數event來控制是否讓窗體關閉。private slots:void documentWasModified();//修改文件private:QString curFile;// 當前文件bool isUntitled;//判斷是否命名bool MaybeSave();//是否保存void SetCurrentFile(const QString &fileName);//設置當前文件QString StrippedName(const QString &fullFilename);// 脫離文件名稱
};

?然后我們就需要在源文件中去實現函數:

3,代碼的實現

3.1MyChild()

首先,在構造函數中,我們需要設置如下:

MyChild::MyChild()
{setAttribute(Qt::WA_DeleteOnClose,true);//關閉窗口時銷毀isUntitled=true;
}

setAttribute用來設置窗口屬性,我們把它設置為:關閉窗口時銷毀。

Qt::WA_DeleteOnClose

  • (1)調用close()方法,會向widget發送一個關閉事件(QCloseEvent),如果widget接受了關閉事件,窗口將會隱藏(實際上調用hide())。如果widget不接受關閉事件,那么窗口將什么也不做。也就是說close()方法只會隱藏窗口對象而已,并不會銷毀該對象。
  • (2)倘若設置了WA_DeleteOnClose屬性,它接收到QCloseEvent事件后,除了調用hide()方法將窗口隱藏外,同時會調用deleteLater()方法將窗口釋放掉,不會再占用資源。

常用的setAttribute窗口屬性 :

//設置為模態框。(如果再設置無邊框窗口,那么模態會失效,不會阻塞其他窗口,須重新設置)
setAttribute(Qt::WA_ShowModal, true);//如果部件接收了關閉事件,則刪除這個部件,相當于delete 
setAttribute(Qt::WA_DeleteOnClose, true);//意思是顯示小部件而不使其處于活動狀態,使它不能獲得焦點
setAttribute(Qt::WA_ShowWithoutActivating,true);//使透明效果生效
setAttribute(Qt::WA_TranslucentBackground);//穿透屬性,可以使部件不可點擊,只顯示外形,適合覆蓋中的部件使用
setAttribute(Qt::WA_TransparentForMouseEvents);//輸入法開關,如果一個編輯框不想讓用戶使用輸入法輸入字符打字,就可以將該屬性設置為false。
setAttribute(Qt::WA_InputMethodEnabled, false);//使用操作系統原生的本地窗口,可以提高兼容性
//但是在linux下,使用該屬性會有問題,因為linux下x11管理的默認原生窗口是白色矩形,就算添加了子部件,也會出現短暫的白色矩形閃爍。
setAttribute(Qt::WA_NativeWindow, true);

3.2 NewFile()

?在新建文件中,我們要保證每次新建文件名稱都不能和上次一樣,就像在電腦上,你多次點擊新建文件 ,他會出現新建文件夾1,2,3.。。。等等。

所以我們要使用靜態變量去實現:static局部變量只初始化一次,在函數退出時它不死亡,下次的運算依據是上一次的結果值。

void MyChild::NewFile()// 新建word文件
{static int sequenceNumber=1;isUntitled=true;curFile=tr("Word文檔-%1").arg(sequenceNumber++);setWindowTitle(curFile);//把新建的名稱設置到標題
}

3.3導入文件LoadFile()

在導入文件中,我們需要先進行判斷,保證導入的不能為空,而且不能為只讀文件。創建一個QByteArray數組來獲取file讀取到的所有文件,然后進行篩選,QTextCodecs 可以用于將一些本地編碼的字符串轉換為 Unicode。

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” )和傳統的8位 “\ 0” 端接字符串 .QByteArray data=file.readAll();// Qt 使用 Unicode 來存儲、繪制、操作字符串。 在許多情況下,可能希望處理使用不同編碼的數據。QTextCodec *codec=Qt::codecForHtml(data);QString str=codec->toUnicode(data);if(Qt::mightBeRichText(str)){//如果是富文本就設置為HTMLthis->setHtml(str);}else{//否則不是富文本設置為簡單字符串格式str=QString::fromLocal8Bit(data);this->setPlainText(str);}SetCurrentFile(filename);connect(document(),SIGNAL(contentsChanged()),this,SLOT(documentWasModified()));}
}

?對轉化而來的字符串str進行判斷,如果是富文本,就設置為HTML,如果不是,就設置為簡單格式。

然后把打開的文件設置為當前文件。

再連接個槽函數,當問將發生改變時,調用修改文件函數。

3.4 保存文件

保存文件的時候需要判斷是直接保存,還是需要另存為保存。

另存為:

  •  從文件對話框獲取文件路徑。
  •  如果路徑不為空,則保存文件saveFile()
bool MyChild::Save()// 保存
{if(isUntitled){return SaveAs();}else{return SaveFile(curFile);}
}bool MyChild::SaveAs()// 另存為
{QString filename=QFileDialog::getSaveFileName(this,tr("另存為"),curFile,tr("HTML 文檔(*.html *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))){//默認保存為 HTML 文檔filename+=".html";}QTextDocumentWriter writer(filename);bool success=writer.write(this->document());if(success){SetCurrentFile(filename);}return success;
}

在保存文件這個SaveFile()函數里,我們首先就是判斷,文件結尾是否帶有.htm或者.html后綴,如果字符串的結尾引用.htm或者.html則返回true,否則返回false。忽略大小寫

CaseInsensitive:不區分大小寫,沒有后綴的話,我們需要加上。

QTextDocumentWriter:用于將QTextDocument寫入文件或其他設備的與格式無關的接口。

?保存文件對話框(對于某些格式QTextDocumentWriter可直接保存,其他不支持的格式就用QTextStream以流的形式保存 )

3.5 關閉文件closeEvent()

void MyChild::closeEvent(QCloseEvent *event)
{if(MaybeSave()){event->accept();}else{event->ignore();}
}

?MaybeSave()

bool MyChild::MaybeSave()//判斷是否修改且保存文檔
{if(!document()->isModified()){return true;}QMessageBox::StandardButton ret;ret=QMessageBox::warning(this,tr("Qt Word"),tr("文件'%1'已經被修改,是否保存?").arg(userFriendlyCurrentFile()),QMessageBox::Save|QMessageBox::Discard|QMessageBox::Cancel);if(ret=QMessageBox::Save){return Save();}else if(ret==QMessageBox::Discard){return false;}return true;
}

我們先進行判斷文件是否被修改過, 是的話返回true;?

3.6 修改文件documentWasModified()

文件更改標簽

?編輯器內容是否被更改,可以使用QTextDocument類的isModified()函數獲知,這里使用了QTextEdit類,document()函數來獲取

?它的QTextDocument類對象。然后使用setWindowModified()函數設置窗口的更改狀態標志“*”,如果參數為true,則將在標題中設置了“[*]”號的地方顯示“*”號,表示該文件已經被修改。

void MyChild::documentWasModified()
{//在設置改變的時候,設置窗口已修改setWindowModified(document()->isModified());
}
QString MyChild::userFriendlyCurrentFile()
{return StrippedName(curFile);
}
QString MyChild::StrippedName(const QString &fullFilename)
{return QFileInfo(fullFilename).fileName();
}

3.7設置當前文件:SetCurrentFile

?canonicalFilePath ()可以除去路徑中符號鏈接,如“.”和“..”等符號。這個函數只是將加載文件的路徑首先保存到curFile中,然后再進行一些狀態的設置

void MyChild::SetCurrentFile(const QString &fileName)// 設置當前文件
{curFile=QFileInfo(fileName).canonicalFilePath();isUntitled=false; //文件已經被保存過document()->setModified(false);//文檔沒有被更改過setWindowModified(false);//窗口不顯示被更改標志setWindowTitle(userFriendlyCurrentFile()+"[*]");//設置窗口標題,userFriendlyCurrentFile ()返回文件名
}

3.8 格式字體設置MergeFormationOnWordOrSelection

?先定位光標就是當前選中文本光標位置。
WordUnderCursor:選擇光標下的單詞。如果光標不在可選字符的字符串中,則不選擇任何文本。

設置字體格式,調用QTextCursor的mergeCharFormat()函數,將參數format所表示的格式應用到光標所在處的字符上
void MyChild::MergeFormationOnWordOrSelection(const QTextCharFormat &format)
{QTextCursor cursor=this->textCursor();if(!(cursor.hasSelection())){cursor.select(QTextCursor::WordUnderCursor);}cursor.mergeCharFormat(format);this->mergeCurrentCharFormat(format);
}

?3.9設置段落對齊格式SetAlign

void MyChild::SetAlign(int align)
{if(align==1){this->setAlignment(Qt::AlignLeft|Qt::AlignAbsolute);//水平靠左}else if(align==2){this->setAlignment(Qt::AlignCenter);//水平方向居中}else if(align==3){this->setAlignment(Qt::AlignRight|Qt::AlignAbsolute);//}else if(align==4){this->setAlignment(Qt::AlignJustify);//水平方向兩端對齊}
}

?設置文本光標,執行文本首部,QTextListFormat 主要用于描述文本符號,編號的格式。

//段落編號
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;//阿拉伯數字break;case 5:stylename=QTextListFormat::ListLowerAlpha;//拉丁字符小寫break;case 6:stylename=QTextListFormat::ListUpperAlpha;//拉丁字符大寫break;case 7:stylename=QTextListFormat::ListLowerRoman;//小寫羅馬數字break;case 8:stylename=QTextListFormat::ListUpperRoman;//大寫羅馬break;}cursor.beginEditBlock();QTextBlockFormat blockFmt=cursor.blockFormat();QTextListFormat listFmt;if(cursor.currentList()){listFmt=cursor.currentList()->format();}else{listFmt.setIndent(blockFmt.indent()+1);blockFmt.setIndent(0);cursor.setBlockFormat(blockFmt);}listFmt.setStyle(stylename);cursor.createList(listFmt);cursor.endEditBlock();}else{QTextBlockFormat bfmt;bfmt.setObjectIndex(-1);cursor.mergeBlockFormat(bfmt);}
}

?

cursor.beginEditBlock()是Qt中的一個函數,用于開始編輯操作的分組,以便在多個編輯操作之間進行撤銷和重做。調用此函數后,任何對文本的更改都將被視為一組操作。只有在調用了cursor.endEditBlock()函數之后,才會結束此組操作,之后才能進行下一個操作分組。

此代碼段中,

  • 首先通過cursor.blockFormat()獲取當前光標所在文本塊的格式,并通過cursor.currentList()判斷當前光標所在的文本塊是否是列表。
  • 如果是,則獲取列表格式并賦值給listFmt變量;
  • 如果不是,則設置listFmt的縮進為當前文本塊縮進加1,并將文本塊縮進設置為0。
  • 然后通過cursor.createList(listFmt)創建一個新的列表,并將樣式設置為stylename
  • 最后調用cursor.endEditBlock()函數結束此次編輯分組。

?如果style=0:

創建一個QTextBlockFormat對象,bfmt的對象索引設置為-1,
用bfmt塊格式修改當前塊(或包含在選擇中的所有塊)的塊格式。

感謝閱讀!!!!!

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11466.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11466.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11466.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何判斷海外住宅ip的好壞?

在海外IP代理中&#xff0c;住宅IP屬于相對較好的資源&#xff0c;無論是用于工作、學習、還是娛樂&#xff0c;都能得到較好的使用效果。作為用戶&#xff0c;該如何判斷海外住宅IP的好壞呢&#xff1f; 穩定性與可靠性&#xff1a;海外住宅IP相比動態IP地址&#xff0c;通常具…

Java全局異常處理,@ControllerAdvice異常攔截原理解析【簡單易懂】

https://www.bilibili.com/video/BV1sS411c7Mo 文章目錄 一、全局異常處理器的類型1-1、實現方式一1-2、實現方式二 二、全局異常攔截點2-1、入口2-2、全局異常攔截器是如何注入到 DispatcherServlet 的 三、ControllerAdvice 如何解析、執行3-1、解析3-2、執行 四、其它4-1、設…

電腦提示找不到ffmpeg.dll無法繼續執行代碼怎么辦?

電腦提示找不到找不到ffmpeg.dll無法繼續執行代碼怎么辦&#xff0c;有什么好的解決辦法&#xff0c;出現這樣的彈出就會導致軟件無法打開或者是異常關閉&#xff0c;找不到dll文件&#xff0c;是一個非常重要的電腦使用問題&#xff0c;會給使用者帶來許多的麻煩。那么找不到d…

LeetCode746:使用最小花費爬樓梯

題目描述 給你一個整數數組 cost &#xff0c;其中 cost[i] 是從樓梯第 i 個臺階向上爬需要支付的費用。一旦你支付此費用&#xff0c;即可選擇向上爬一個或者兩個臺階。 你可以選擇從下標為 0 或下標為 1 的臺階開始爬樓梯。 請你計算并返回達到樓梯頂部的最低花費。 代碼 …

MongoDB和AI 賦能行業應用:制造業和汽車行業

歡迎閱讀“MongoDB和AI 賦能行業應用”系列的第一篇。 本系列重點介紹AI應用于不同行業的關鍵用例&#xff0c;涵蓋制造業和汽車行業、金融服務、零售、電信和媒體、保險以及醫療保健行業。 隨著人工智能&#xff08;AI&#xff09;在制造業和汽車行業的集成&#xff0c;傳統…

CDN的工作原理及流程

CDN&#xff08;Content Delivery Network&#xff0c;內容分發網絡&#xff09;是一種構建在數據網絡上的分布式內容分發網絡。 CDN利用全局負載均衡技術&#xff0c;將用戶的訪問請求指向離用戶最近且工作正常的流媒體服務器上&#xff0c;由流媒體服務器直接響應用戶的請求…

Tableau學習2.0版——復習

官網下載鏈接&#xff1a;https://www.tableau.com/zh-cn/support/releases 學生賬戶申請鏈接&#xff1a;https://www.tableau.com/zh-cn/academic/students。直接去學信網下載學籍在線驗證作為申請證明。 目錄 1、可視化原理 2、基礎圖表制作 2.1 對比分析&#xff08;比…

@游戲行業er!MongoDB廣州線下沙龍邀您報名!

隨著游戲和應用程序的發展&#xff0c;數據變得越來越重要。在為您的下一個游戲選擇數據庫時&#xff0c;數據庫管理者常常會面對靈活性、可擴展性、可靠性、運營效率等問題或挑戰。 MongoDB在游戲開發領域有著廣泛的應用&#xff0c;靈活數據模型可以存儲和處理各種類型的數據…

JPA ENTITY EXTEND

1. Overview Relational databases don’t have a straightforward way to map class hierarchies onto database tables. To address this, the JPA specification provides several strategies: MappedSuperclass – the parent classes, can’t be entitiesSingle Table …

webpack處理js和css模塊化導入導出示例:

webpack默認并不能處理js模塊化的導入和導出,依賴于ts-loader和babel-loader webpack.config,js module.exports {entry: ./src/index.ts,output: {filename: main.js,},mode: development, // 或者 productionmodule: {rules: [{test: /\.ts/,exclude: /(node_modules)/,use:…

二維平移矩陣 (2D translate matrix)

2D translate matrix 推薦閱讀正文推薦閱讀 矢量旋轉矩陣 正文 之前我們介紹了矢量旋轉矩陣的形式,這里我們來介紹一下平移矩陣的形式。比如,我們我們有一個點,其坐標為 (0,1)。那么我們如何操作才能夠將這個點沿著 x 軸正方向平移 1 個單位長度呢? 這里我們以向右移動…

vj題單 P4552 [Poetize6] IncDec Sequence

思路&#xff1a; 一次操作&#xff1a;選一個區間[l, r]&#xff0c;把這個區間的數都加1或者都減1&#xff0c;可以將求該數列的差分數組b然后來進行該操作 一次操作的兩種種情況&#xff1a;&#xff08;l可以等于r&#xff09; 1.b[l]1 b[r1]-1 2.b[l]-1 b[r1]1 Q1:…

PHP 提取數組中的特定的值

需求&#xff1a; 前端展示&#xff1a; &#xff08;1&#xff09;之前的頁面&#xff1a; &#xff08;2&#xff09;修改后的頁面&#xff1a; 之前接口返回的數據 &#xff1a; 解決辦法&#xff1a;提取tags 中的 ’約 的數組 添加到一個新的數組中去 1&#xff1a;一開…

【CPP】多線程并發—— Mutex 和 Lock

#include <iostream> #include <thread> #include <mutex> #include "my_utils.h"std::mutex mtx; // 全局互斥鎖 int shared_data 0; // 共享數據 void increment() { for (int i 0; i < 10; i) { std::cout <<"incre…

2024年去除視頻水印的5種方法

如果你從事電影剪輯或者視頻編輯工作&#xff0c;你經常需要從優酷、抖音、TikTok下載各種視頻片段……。 通常這些視頻帶有水印和字幕。一些免費軟件如CapCut、canva、Filmora也會給你制作的視頻打上水印&#xff0c;這些水印嵌入在視頻內部。 2024年去除視頻水印的5種方法 …

Mysql-用戶變量的聲明與使用

#聲明變量 #1.標識符不能以數字開頭 #2.只能使用_或$符號&#xff0c;不能使用其他符號 #3.不能使用系統關鍵字 setuserName劉德華; select userName:劉青云;#將賦值與查詢結合 #查詢變量、使用變量&#xff0c;匿名的時候建議加上as select userName as 讀取到的userName變量…

Golang面向對象編程(二)

文章目錄 封裝基本介紹封裝的實現工廠函數 繼承基本介紹繼承的實現字段和方法訪問細節多繼承 封裝 基本介紹 基本介紹 封裝&#xff08;Encapsulation&#xff09;是面向對象編程&#xff08;OOP&#xff09;中的一種重要概念&#xff0c;封裝通過將數據和相關的方法組合在一起…

java JOptionPane 介紹

JOptionPane是Java Swing庫中的一個類,用于創建對話框(Dialogs),以便與用戶進行交互。它提供了一種簡單的方式來顯示消息、警告、錯誤、輸入框等。 主要方法: showMessageDialog(Component parentComponent, Object message):顯示一個包含消息的對話框。showInputDialog…

2024OD機試卷-手機App防沉迷系統 (java\python\c++)

題目:手機App防沉迷系統 題目描述 智能手機方便了我們生活的同時,也侵占了我們不少的時間。 “手機App防沉迷系統”能夠讓我們每天合理地規劃手機App使用時間,在正確的時間做正確的事。 它的大概原理是這樣的: 在一天24小時內,可以注冊每個App的允許使用時段一個時間段只…

Java轉Kotlin調用JNI方法異常

一、背景 Java調用JNI方法時沒有任何問題&#xff0c;但是使用Java轉Kotlin以后出現了崩潰異常&#xff1a;A java_vm_ext.cc:597] JNI DETECTED ERROR IN APPLICATION: jclass has wrong type: 校驗參數后沒有任何變化&#xff0c;經過分析驗證找到解決方案 二、原因…