在之前的三集中我们已经将窗口的整体效果制作的差不多了,在我们新建很多编辑文档的时候,他们也会出现在文本编辑区,担是有个问题就是没有滚动条,当文本编辑框很大的时候不能通过拉伸窗口来实现文本框也扩大队不对?接下来就是对文本编辑器进行一些功能上面的优化。
添加滚动条:
在mainwindow.cpp的初始化函数函数InitMainWindow()添加代码
//添加滚动条
ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);//设置垂直滚动条,属性设置为当在需要的时候显示
ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);//设置水平滚动条,属性设置为当需要的时候显示
//注意要点在设置滚动条的时候碰到一个小bug,就是在你要设置滚动条的时候要留出一点位置给水平滚动条,不然无法显示
Qt中有两种添加滚动条的函数,一个是setVerticalScrollBarPolicy设置垂直滚动条,一个是setHorizontalScrollBarPolicy设置水平滚动条。
注意的是:在设置水平滚动条文本编辑区mdiArea最好在下方留一点位置,不然可能会添加不上水平滚动条。
设置主窗口的图标和标题:
其实设置图标非常简单只需要在项目的pro文件里添加一行代码就可以
当然后面的文件名是要自己设置的
设置文件标题,可以通过setWindowtitle的方法实现,也可以通过在设计文件的windowtitle设置窗口标题。
刷新工具栏状态:
我们创建一个文本窗口的时候工具栏的可用状态是有区别的,创建一个空文档的时候这是像打印还有保存另存,关闭是应该可用的,但是粘贴还有加粗左对齐右对齐什么应该是选中文本的时候才可以用
添加槽函数:
private slots:
void RefreshMenu();//刷新窗口菜单槽函数
在初始化主窗口函数建立连接:(InitmainWindows())
RefreshMenu();//在初始化的时候调用一下首先对工具栏进行初始化、
//当有活动子窗口的时候,发射信号刷新菜单
connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::RefreshMenu);
这样如果文本编辑区mdiArea有子窗口的时候会自动调用RefreshMenu()函数,刷新工具栏;还有的应该注意到,我们在链接之前就调用了一次函数,这么做也是为了初始化。
RefreshMenu()函数实现:
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);
}
刷新工具栏操作,首先要判断是否有活动子窗口,如果有设置一些的功能可用,在接着判断是否有文本被选中,如果有紧接着在将一些功能设置为可用。文本是否选中可以利用函数textCursor().hasSelection()判断,它会返回文本信息,不为0,就是选中了。
activateChildWad()函数声明:(在mainWindows.h)
private:
void formatEnable();
ChildWnd* activateChildWnd();//获取mdieara的活动子窗口
activateChildWnd()函数实现:
ChildWnd *MainWindow::activateChildWnd()
{
QMdiSubWindow*actWnd=ui->mdiArea->activeSubWindow();//获取文档编辑器的活动窗口
if(actWnd)
return qobject_cast<ChildWnd*>(actWnd->widget());//如果是活动窗口那就将这个窗口转换为目标的窗口并且返回
else
return 0;
}
刷新窗口菜单(显示窗体名字)
思路是当我们的编辑区有很多个编辑文档的时候,点击窗口时候应该在窗口菜单中显示文本编辑窗口的名字。
这个时候我们首先就会想到信号与槽。
添加槽函数声明:
private slots:
void addSubWindowListMenu();//刷新窗口信息函数
槽函数实现:
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函数返回窗口是否为活动子窗口的状态
}
formatEnable();
}
这里解释一下大概,我们的思路是当我们点击窗口菜单时候,主窗口应该自动响应槽函数,而这个槽函数的作用就是每次点击先将所有的子菜单选项都删除,紧接着再将原来应该在菜单里的子菜单选项都加进去。之后呢,利用一个链表将mdiArea的子窗口全部添加进链表里,利用自带函数subWindowList实现。再利用循环一次次的将链表里的QMdiSubWindow转换成ChildWnd也就是文本编辑框,再利用QString 的arg函数帮窗口取名字方便后面利用addAction函数尽心添加子菜单选项。
setCheckable函数是可以设置能否勾选,setChecked是设置勾选状态,当然我们传进的一个bool值看是不是活动子窗口设置勾选。之后再调用formatEnable()函数实现工具栏是否可用
槽函数链接与调用:
在InitMainWindows()函数里添加调用代码:
addSubWindowListMenu();
//这里我们需要另外的槽函数相应,窗口点击的时候有新窗口内容添加进去
connect(ui->menu_W,&QMenu::aboutToShow,this,&MainWindow::addSubWindowListMenu);
实现窗口菜单栏内子菜单的功能:
在设计模式里为Action创建槽函数
槽函数声明:
private slots:
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 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();//前一个窗口
}
关闭窗口事件
函数声明:
protected:
void closeEvent(QCloseEvent *event);
函数实现:
void MainWindow::closeEvent(QCloseEvent *event){
ui->mdiArea->closeAllSubWindows();//当窗口关闭的时候当然子窗口也必须关闭
if(ui->mdiArea->currentSubWindow())
event->ignore();//忽略此事件
else
event->accept();//接受此事件
}
效果实现:
之后的窗口菜单的子菜单功能已全部实现,这里就不展示效果了。
代码:
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();//从文件路径中提取文档名
private slots:
void docBeModified(); //文档修改时相应的槽函数
private:
bool m_bSaved; //文档是否保存
};
#endif // CHILDWND_H
childWnd.cpp
#include "childwnd.h"
#include<QFileInfo>
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操作,返回从文件路径提取的文件名
}
void ChildWnd::docBeModified()
{
setWindowModified(document()->isModified());
}
mainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include"childwnd.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
void InitMainWindow();//初始化窗口函数
void DocNew();
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();
protected:
void closeEvent(QCloseEvent *event);
private:
void formatEnable();
ChildWnd* activateChildWnd();//获取mdieara的活动子窗口
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include"childwnd.h"
#include<QDebug>
#include<QMdiSubWindow>
#include<QList>
#include<QCloseEvent>
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);
}
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::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;
}
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函数返回窗口是否为活动子窗口的状态
}
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::closeEvent(QCloseEvent *event){
ui->mdiArea->closeAllSubWindows();//当窗口关闭的时候当然子窗口也必须关闭
if(ui->mdiArea->currentSubWindow())
event->ignore();//忽略此事件
else
event->accept();//接受此事件
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}