之前我们实现的都是对文本框或则说是对文本的一些功能实现,接下来我们着重去实现对段落的一些操作。对段落的一些操作我们想到就是加粗,切斜,还有下划线。
向ChildWnd.h添加函数:
public:
void setFormatOnSelectedWord(const QTextCharFormat& fmt);
设置这个函数的实质其实也是为了与主窗口的一些函数建立连接,它实质就是一个设置格式函数,而字段的一些格式设置,其实都是属于ChildWnd类的。
主要思想就是主窗口是表面调用,子窗口才是实质性的设置。
SetFormatOnSelectedWord()函数实现:
void ChildWnd::setFormatOnSelectedWord(const QTextCharFormat &fmt)
{
QTextCursor tCursor=textCursor();
//没有选中将光标下的文字设置选中
if(!tCursor.hasSelection()) tCursor.select(QTextCursor::WordUnderCursor);
//已将选中了,合并文本格式
tCursor.mergeCharFormat(fmt);//将光标选中的文本与选择的格式进行合并
mergeCurrentCharFormat(fmt);//将之后的文本编辑器格式设置为光标选中文本
}
函数会首先获取游标的副本,再判断游标指示的内容有没有被选中,没有选中则设置为选中的光标下的文本。
如果已经选中文本内容,那么将光标选中的文本与选择的格式进行合并,也就是tCursor.mergeCharFormat函数。而mergeCurrentCharFormat函数则将整个文本框的格式设置为选中的格式。
向主窗口添加函数(mainWindow.h):
public:
void textBold();//加粗
void textItalic();//切斜
void textUnderline();//下划线
函数实现:
void MainWindow::textBold()
{
QTextCharFormat fmt;
fmt.setFontWeight(ui->boldAction->isChecked()?QFont::Bold:QFont::Normal);
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);
}
}
void MainWindow::textItalic()
{
QTextCharFormat fmt;
fmt.setFontItalic(ui->italicAction->isChecked());
//判断为活动子窗口
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);
}
}
void MainWindow::textUnderline()
{
QTextCharFormat fmt;
fmt.setFontUnderline(ui->underlineAction->isChecked());
//判断是否为活动子窗口
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);//根据选择的格式设置格式
}
}
这些函数都是先定义一个格式类QTextCharFormat 对象然后利用setFontWeight方法设置文本格式。再判断这个窗口是不是活动窗口,如果是那就调用子类真正设置文本格式的函数,应用到活动子窗口。
在ui文件里为Action(加粗,倾斜,下划线)添加槽函数:
private slots:
void on_boldAction_triggered();
void on_italicAction_triggered();
void on_underlineAction_triggered();
函数实现:
void MainWindow::on_boldAction_triggered()
{
textBold();
}
void MainWindow::on_italicAction_triggered()
{
textItalic();
}
void MainWindow::on_underlineAction_triggered()
{
textUnderline();
}
槽函数再调用主窗口函数,主窗口函数再调用子窗口函数。
程序效果:
源码:
ChildWnd.h:
#ifndef CHILDWND_H
#define CHILDWND_H
#include<QTextEdit>
//思路就是主窗体创建一个子窗口编辑区,就是调用另外一个类的构造函数,那么就是ChildWnd这个类
class ChildWnd : public QTextEdit
{
Q_OBJECT
public:
ChildWnd();
QString m_Currentpath; //当前文档的路径
void newDoc(); //新建文档
QString GetCurDocName();//从文件路径中提取文档名
bool loadDoc(const QString& docName);
void setCurDoc(const QString& docName);//设置当前文档函数
bool saveDoc();//保存文档
bool saveAsDoc();//另存为文档
bool saveDocOpt(QString &docName);//真正执行保存操作
void setFormatOnSelectedWord(const QTextCharFormat& fmt);
protected:
void closeEvent(QCloseEvent *event);//关闭事件处理
private:
bool promptSave();//尝试保存
private slots:
void docBeModified(); //文档修改时相应的槽函数
private:
bool m_bSaved; //文档是否保存
};
#endif // CHILDWND_H
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"childwnd.h"
#include<QSignalMapper>
#include<QMdiSubWindow>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void InitMainWindow();//初始化窗口函数
void DocNew();
void DocOpen();
void docSave();
void docSaveAs();
void docUndo();//撤销
void docRedo();//重写
void docCut();//剪切
void docCopy();//复制
void docPaste();//粘贴
void textBold();//加粗
void textItalic();//切斜
void textUnderline();//下划线
private slots:
void on_newAction_triggered();
void RefreshMenu();//刷新窗口菜单槽函数
void addSubWindowListMenu();//刷新窗口信息函数
void on_closeAction_triggered();
void on_closeAllAction_triggered();
void on_titleAction_triggered();
void on_cascadeAction_triggered();
void on_nextAction_triggered();
void on_previousAction_triggered();
void setActivSubWindow(QWidget*wnd);
void on_openAction_triggered();
void on_saveAction_triggered();
void on_saveAsAction_triggered();
void on_undoAction_triggered();
void on_redoAction_triggered();
void on_cutAction_triggered();
void on_copyAction_triggered();
void on_pasteAction_triggered();
void on_boldAction_triggered();
void on_italicAction_triggered();
void on_underlineAction_triggered();
protected:
void closeEvent(QCloseEvent *event);
private:
void formatEnable();
ChildWnd* activateChildWnd();//获取mdieara的活动子窗口
//查找窗口函数
QMdiSubWindow* FindChildWnd(const QString& docName);
private:
Ui::MainWindow *ui;
QSignalMapper* m_singnalmapper;// 创建一个信号映射器
};
#endif // MAINWINDOW_H
ChildWnd.cpp
#include "childwnd.h"
#include<QFileInfo>
#include<QFileDialog>
#include<QTextDocumentWriter>
#include<QMessageBox>
#include<QCloseEvent>
ChildWnd::ChildWnd()
{
//子窗口关闭时销毁类的实例对象,达到回收
setAttribute(Qt::WA_DeleteOnClose);
m_bSaved=false;//初始的时候编辑状态的未改变的
}
void ChildWnd::newDoc()
{
static int wndSeqNum=1;
m_Currentpath=QString("WPS 文档 %1").arg(wndSeqNum++);//每次创建文档的时候是名字不相同的,,利用wndSeqNum来达到目标
//设置窗体标题,文档改动后加“*”号标识
setWindowTitle(m_Currentpath+"[*]"+"-MyWPS");
connect(document(),SIGNAL(contentsChanged()),
this,SLOT(docBeModified()));
}
QString ChildWnd::GetCurDocName()
{
return QFileInfo(m_Currentpath).fileName();//fileINfo类可以根据文件调用filename操作,返回从文件路径提取的文件名
}
bool ChildWnd::loadDoc(const QString &docName)
{
//先判断加载进来的文档名字是否为空
if(!docName.isEmpty()){
QFile file(docName);//通过文件名获取文件的所有信息
if(!file.exists()) return false;//文件不存在返回false
if(!file.open(QFile::ReadOnly)) return false;//只读方式打开失败返回false
QByteArray text=file.readAll();
if(Qt::mightBeRichText(text)){
setHtml(text);//如果是富文本就设置为HTml
}else{
setPlainText(text);//如果不是富文本设置为简单字符串格式
}
setCurDoc(docName);
connect(document(),SIGNAL(contentsChanged()),
this,SLOT(docBeModified()));
return true;
}
}
void ChildWnd::setCurDoc(const QString &docName)
{
m_Currentpath=QFileInfo(docName).canonicalFilePath();//它会将文件的路径名称的“。”都去除
m_bSaved=true;
document()->setModified(false);//将文档是否被修改属性设置为false
setWindowModified(false); //窗口不显示改动标识
setWindowTitle(GetCurDocName()+"[*]");//设置子窗口标题
}
bool ChildWnd::saveDoc()
{
if(m_bSaved) return saveDocOpt(m_Currentpath);//保存过直接进行保存操作
else saveAsDoc();//另存为
}
bool ChildWnd::saveAsDoc()
{
//另存为当然是提供一个文件窗口来提示保存
QString docName=QFileDialog::getSaveFileName(this,
"另存为",
m_Currentpath,
"HTML文档 (*.htm *html);;"
"所有文件(*.*)");
if(docName.isEmpty()) return false;
else return saveDocOpt(docName);
}
bool ChildWnd::saveDocOpt(QString &docName)
{
if(!(docName.endsWith(".htm",Qt::CaseInsensitive)||
docName.endsWith(".html",Qt::CaseInsensitive))){
//如果不是以指定格式结尾,添加指定格式
docName+=".html";
}
QTextDocumentWriter writer(docName);
bool isSuccess=writer.write(this->document());//write方法会返回写的状态
if(isSuccess) setCurDoc(docName);//调研设置窗口状态函数
return isSuccess;
}
void ChildWnd::setFormatOnSelectedWord(const QTextCharFormat &fmt)
{
QTextCursor tCursor=textCursor();
//没有选中将光标下的文字设置选中
if(!tCursor.hasSelection()) tCursor.select(QTextCursor::WordUnderCursor);
//已将选中了,合并文本格式
tCursor.mergeCharFormat(fmt);//将光标选中的文本与选择的格式进行合并
mergeCurrentCharFormat(fmt);//将之后的文本编辑器格式设置为光标选中文本
}
void ChildWnd::closeEvent(QCloseEvent *event)
{
if(promptSave()) event->accept();//接受事件
else event->ignore();//忽略事件
}
bool ChildWnd::promptSave()
{
//文档没有被修改
if(!document()->isModified()) return true;
QMessageBox::StandardButton result;
result=QMessageBox::warning(this,
QString("系统提示"),
QString("文档%1已修改,是否保存?")
.arg(GetCurDocName()),
QMessageBox::Yes|
QMessageBox::Discard|
QMessageBox::No);
if(result==QMessageBox::Yes){
return saveDoc();
}else if(result==QMessageBox::No){
return false;
}
return true;//选择忽略就返回真
}
void ChildWnd::docBeModified()
{
setWindowModified(document()->isModified());
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include"childwnd.h"
#include<QDebug>
#include<QMdiSubWindow>
#include<QList>
#include<QCloseEvent>
#include<QFileDialog>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
InitMainWindow();
}
MainWindow::~MainWindow()
{
delete ui;
}
//初始化窗口函数
void MainWindow::InitMainWindow()
{
//初始化字符列表
QFontDatabase fontdb;
foreach (int fontsize, fontdb.standardSizes()) {
//standarSizes函数返回的是标准的字符列表。这里是每次将标准字符的大小
//传递给fontsize
ui->FontSize_comboBox->addItem(QString::number(fontsize));//将每次获取到的字符传给组件列表
}
QFont defFont;//程序默认的字体(包括字号和字体样式)
QString sFontSize;
int defFontSize;//当前应用程序默认的字体字号
int defFontindex;//当前字号表的序号
defFont=QApplication::font();//获取当前程序的字体样式
defFontSize=defFont.pointSize();//返回当前字体字号
sFontSize=QString::number(defFontSize);//将整形的字号转换为字符串类型的方便列表查找序号
defFontindex=ui->FontSize_comboBox->findText(sFontSize);//将默认的字号对应的序号返回
ui->FontSize_comboBox->setCurrentIndex(defFontindex);//设置默认程序下的字号序号
//添加滚动条
ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);//设置垂直滚动条,属性设置为当在需要的时候显示
ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);//设置水平滚动条,属性设置为当需要的时候显示
//注意要点在设置滚动条的时候碰到一个小bug,就是在你要设置滚动条的时候要留出一点位置给水平滚动条,不然无法显示
RefreshMenu();//在初始化的时候调用一下首先对工具栏进行初始化、
//当有活动子窗口的时候,发射信号刷新菜单
connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::RefreshMenu);
addSubWindowListMenu();
//这里我们需要另外的槽函数相应,窗口点击的时候有新窗口内容添加进去
connect(ui->menu_W,&QMenu::aboutToShow,this,&MainWindow::addSubWindowListMenu);
//初始化信号映射器
m_singnalmapper=new QSignalMapper(this);
connect(m_singnalmapper,SIGNAL(mapped(QWidget*)),
this,SLOT(setActivSubWindow(QWidget*)));
}
void MainWindow::DocNew()
{
ChildWnd* childwnd=new ChildWnd;
ui->mdiArea->addSubWindow(childwnd);//将新建的子窗口添加到编辑区
//同时将赋值和剪切的Action建立连接
connect(childwnd,SIGNAL(copyAvailable(bool)),ui->cutAction,SLOT(setEnabled(bool)));
connect(childwnd,SIGNAL(copyAvailable(bool)),ui->copyAction,SLOT(setEnabled(bool)));
childwnd->newDoc();
childwnd->show();
formatEnable();//新建窗口后将工具栏设置为可用
}
void MainWindow::DocOpen()
{
QString docName=QFileDialog::getOpenFileName(this,
"打开文件",
"",
"文本文件(*.txt);;"
"HTML文件(*.html *.htm);;"
"所有文件(*.*)");
if(!docName.isEmpty()){
QMdiSubWindow* existWnd=FindChildWnd(docName);
if(existWnd){
//如果找到这个子窗口
ui->mdiArea->setActiveSubWindow(existWnd);//将这个窗口设置为活动窗口
return;
}
ChildWnd* childWnd=new ChildWnd;
ui->mdiArea->addSubWindow(childWnd);
connect(childWnd,SIGNAL(copyAvailable(bool)),
ui->cutAction,SLOT(setEnabled(bool)));//copyAvailable是在打开文档选中文本的时候触发
connect(childWnd,SIGNAL(copyAvailable(bool)),
ui->copyAction,SLOT(setEnabled(bool)));
if(childWnd->loadDoc(docName)){
statusBar()->showMessage("文件已打开",3000);
childWnd->show();
formatEnable();
}else{
childWnd->close();
}
}
}
void MainWindow::docSave()
{
if(activateChildWnd()&&activateChildWnd()->saveDoc()){
statusBar()->showMessage("保存成功",3000);
}
}
void MainWindow::docSaveAs()
{
if(activateChildWnd()&&activateChildWnd()->saveAsDoc()){
statusBar()->showMessage("保存成功",3000);
}
}
void MainWindow::docUndo()
{
if(activateChildWnd()){
activateChildWnd()->undo();
}
}
void MainWindow::docRedo()
{
if(activateChildWnd()){
activateChildWnd()->redo();
}
}
void MainWindow::docCut()
{
if(activateChildWnd()){
activateChildWnd()->cut();
}
}
void MainWindow::docCopy()
{
if(activateChildWnd()){
activateChildWnd()->copy();
}
}
void MainWindow::docPaste()
{
if(activateChildWnd()){
activateChildWnd()->paste();
}
}
void MainWindow::textBold()
{
QTextCharFormat fmt;
fmt.setFontWeight(ui->boldAction->isChecked()?QFont::Bold:QFont::Normal);
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);
}
}
void MainWindow::textItalic()
{
QTextCharFormat fmt;
fmt.setFontItalic(ui->italicAction->isChecked());
//判断为活动子窗口
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);
}
}
void MainWindow::textUnderline()
{
QTextCharFormat fmt;
fmt.setFontUnderline(ui->underlineAction->isChecked());
//判断是否为活动子窗口
if(activateChildWnd()){
activateChildWnd()->setFormatOnSelectedWord(fmt);//根据选择的格式设置格式
}
}
void MainWindow::formatEnable()
{
ui->boldAction->setEnabled(true);//将工具栏的功能设置为可用
ui->italicAction->setEnabled(true);
ui->underlineAction->setEnabled(true);
ui->letfAlignAction->setEnabled(true);
ui->centerAlignAction->setEnabled(true);
ui->rightAlignAction->setEnabled(true);
ui->justifyAction->setEnabled(true);
ui->colorAction->setEnabled(true);
//qDebug()<<12%1<<endl;
}
ChildWnd *MainWindow::activateChildWnd()
{
QMdiSubWindow*actWnd=ui->mdiArea->activeSubWindow();//获取文档编辑器的活动窗口
if(actWnd)
return qobject_cast<ChildWnd*>(actWnd->widget());//如果是活动窗口那就将这个窗口转换为目标的窗口并且返回
else
return 0;
}
QMdiSubWindow *MainWindow::FindChildWnd(const QString &docName)
{
QString strFile=QFileInfo(docName).canonicalFilePath();
foreach (QMdiSubWindow* subWnd, ui->mdiArea->subWindowList()) {
ChildWnd* childWnd=qobject_cast<ChildWnd*>(subWnd->widget());//将每一个子窗口转换成ChildWnd类型
if(childWnd->m_Currentpath==strFile)return subWnd;
}
return 0;
}
void MainWindow::on_newAction_triggered()
{
DocNew();//点击新建动作的时候执行新建文档操作
}
void MainWindow::RefreshMenu()
{
bool hasChild;
hasChild=(activateChildWnd()!=0);
ui->saveAction->setEnabled(hasChild);
ui->printAction->setEnabled(hasChild);
ui->saveAsAction->setEnabled(hasChild);
ui->printPreviewAction->setEnabled(hasChild);
ui->pasteAction->setEnabled(hasChild);
ui->closeAction->setEnabled(hasChild);
ui->closeAllAction->setEnabled(hasChild);
ui->titleAction->setEnabled(hasChild);
ui->cascadeAction->setEnabled(hasChild);
ui->nextAction->setEnabled(hasChild);
ui->previousAction->setEnabled(hasChild);
//除了上面的一些文档操作当然还有文本操作
bool hasSelect=(activateChildWnd() && activateChildWnd()->textCursor().hasSelection());//textcursor函数会返回选中文本的信息,这里用这个函数就是检测文本有没有被选中
ui->copyAction->setEnabled(hasSelect);
ui->cutAction->setEnabled(hasSelect);
ui->boldAction->setEnabled(hasSelect);
ui->italicAction->setEnabled(hasSelect);
ui->underlineAction->setEnabled(hasSelect);
ui->letfAlignAction->setEnabled(hasSelect);
ui->centerAlignAction->setEnabled(hasSelect);
ui->rightAlignAction->setEnabled(hasSelect);
ui->colorAction->setEnabled(hasSelect);
ui->justifyAction->setEnabled(hasSelect);
}
void MainWindow::addSubWindowListMenu()
{
//因为每次点击的时候hi执行这个函数,如果不先清空那就会导致这个菜单栏越来越长
ui->menu_W->clear();
ui->menu_W->addAction(ui->closeAction);
ui->menu_W->addAction(ui->closeAllAction);
ui->menu_W->addSeparator();
ui->menu_W->addAction(ui->titleAction);
ui->menu_W->addAction(ui->cascadeAction);
ui->menu_W->addSeparator();
ui->menu_W->addAction(ui->nextAction);
ui->menu_W->addAction(ui->previousAction);
//点击窗口时相应的函数就是这个,那么我们就要判断有没有活动子窗口
QList<QMdiSubWindow*> wnds=ui->mdiArea->subWindowList();//这个函数会将mdiArea内的窗口通过链表的形式返回
if(!wnds.isEmpty()){
ui->menu_W->addSeparator();//如果有活动子窗口那么就添加一条分割线
}
for(int i=0;i<wnds.size();++i){
ChildWnd* childwnd=qobject_cast<ChildWnd*>(wnds.at(i)->widget());//将链表中的元素转换为部件,再用类型转化为ChildWnds
QString menuItem_text;
menuItem_text=QString("%1 %2")
.arg(i+1)
.arg(childwnd->GetCurDocName());
QAction* menuItem_act=ui->menu_W->addAction(menuItem_text);//将窗口的名字添加进menu_w里面
menuItem_act->setCheckable(true);
menuItem_act->setChecked(childwnd==activateChildWnd());//是否选中看是否是活动子窗口,activateChildWnd函数返回窗口是否为活动子窗口的状态
//将每一个添加进去的Action添加信号
connect(menuItem_act,SIGNAL(triggered(bool)),
m_singnalmapper,SLOT(map()));//每一个action菜单当被点击的时候都会触发triggered信号执行map()槽函数
//这样的话都会执行map()函数,但是又怎么区分开呢?
m_singnalmapper->setMapping(menuItem_act,wnds.at(i));//通过设定不同Action的区别,利用序号来区别
}
formatEnable();
}
void MainWindow::on_closeAction_triggered()
{
ui->mdiArea->closeActiveSubWindow();//关闭活动子窗口
}
void MainWindow::on_closeAllAction_triggered()
{
ui->mdiArea->closeAllSubWindows();//关闭所有活动子窗口
}
void MainWindow::on_titleAction_triggered()
{
ui->mdiArea->tileSubWindows();//平铺
}
void MainWindow::on_cascadeAction_triggered()
{
ui->mdiArea->cascadeSubWindows();//cengdie
}
void MainWindow::on_nextAction_triggered()
{
ui->mdiArea->activateNextSubWindow();//下一个窗口
}
void MainWindow::on_previousAction_triggered()
{
ui->mdiArea->activatePreviousSubWindow();//前一个窗口
}
void MainWindow::setActivSubWindow(QWidget*wnd)
{
//首先判断一下是否为活动子窗口
if(!wnd){
return;
}else{
//将文本编辑区的这个窗口设置为活动子窗口
ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(wnd));
//设置活动子窗口是传进一个文本编辑区的mdiSubWindow的,所以这里用到类型转换
}
}
void MainWindow::closeEvent(QCloseEvent *event){
ui->mdiArea->closeAllSubWindows();//当窗口关闭的时候当然子窗口也必须关闭
if(ui->mdiArea->currentSubWindow())
event->ignore();//忽略此事件
else
event->accept();//接受此事件
}
void MainWindow::on_openAction_triggered()
{
DocOpen();//打开文档操作
}
void MainWindow::on_saveAction_triggered()
{
docSave();//保存
}
void MainWindow::on_saveAsAction_triggered()
{
docSaveAs();//另存为
}
void MainWindow::on_undoAction_triggered()
{
docUndo();
}
void MainWindow::on_redoAction_triggered()
{
docRedo();
}
void MainWindow::on_cutAction_triggered()
{
docCut();
}
void MainWindow::on_copyAction_triggered()
{
docCopy();
}
void MainWindow::on_pasteAction_triggered()
{
docPaste();
}
void MainWindow::on_boldAction_triggered()
{
textBold();
}
void MainWindow::on_italicAction_triggered()
{
textItalic();
}
void MainWindow::on_underlineAction_triggered()
{
textUnderline();
}