C++ QT开发 学习笔记(3) - WPS项目
标准对话框
| 对话框类 | 说明 | 静态函数 | 函数说明 | 
|---|---|---|---|
| QFileDialog | 文件对话框 | getOpenFileName() | 选择打开一个文件 | 
| getOpenFileNames() | 选择打开多个文件 | ||
| getSaveFileName() | 选择保存一个文件 | ||
| getExistingDirectory() | 选择一个己有的目录 | ||
| getOpenFileUrl() | 选择打幵一个文件,可选择远程网络文件 | ||
| QColorDialog | 颜色对话框 | getColor() | 选择颜色 | 
| QFontDialog | 字体对话框 | QFont getFont() | 选择字体 | 
| QInputDialog | 输入对话框 | getText() | 输入单行文字 | 
| getlnt() | 输入整数 | ||
| getDouble() | 输入浮点数 | ||
| getltem() | 从一个下拉列表框中选择输入 | ||
| getMultiLineText() | 输入多行字符串 | ||
| QMessageBox | 消息框 | information() | 信息提示对话框 | 
| question() | 询问并获取是否确认的对话框 | ||
| waming() | 警告信息提示对话框 | ||
| critical() | 错误信息提示对话框 | ||
| about() | 设置自定义信息的关于对话框 | ||
| aboutQt() | 关于Qt的对话框 | 
新建桌面应用程序,项目名testStandardDialogs,类名Dialog,基类QDialog,不勾选创建界面文件。
编辑dialog.h文件
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QPushButton>
#include <QLineEdit>
#include <QGridLayout>
#include "inputdlg.h"
#include "msgboxdialog.h"
class Dialog : public QDialog
{
    Q_OBJECT
public:
    Dialog(QWidget *parent = 0);
    ~Dialog();
private:
    QPushButton* m_fileBtn;     //打开文件对话框
    QLineEdit* m_fileLineEdit;  //显示所源文件路径
    QGridLayout* m_mainLayout;  //布局管理器
    QPushButton *m_ColorBtn;    //打开颜色对话框
    QFrame* m_ColorFrame;       //显示所愿颜色效果
    QPushButton* m_fontBtn;     //打开字体对话框
    QLineEdit* m_fontLineEdit;  //显示所选字体效果
    QPushButton* m_inputBtn;    //显示输入对话框
    InputDlg* m_inputDlg;       //自定义InputDlg 对象
    QPushButton* m_msgBtn;      //显示消息对话框
    MsgBoxDialog* m_msgboxDlg;  //消息对话框
    QPushButton* m_customBtn;   //自定义消息框
    QLabel* m_customLabel;      //自定义标签
private slots:
    void ShowFileDlg();
    void ShowColorDlg();
    void ShowFontDlg();
    void ShowInputDlg();
    void ShowMsgDlg();
    void ShowCustomDlg();
};
#endif // DIALOG_H
编辑dialog.cpp编辑构造函数及添加相应成员函数的定义
#include "dialog.h"
#include <QFileDialog>
#include <QColorDialog>
#include <QFontDialog>
#include <QMessageBox>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
{
    setWindowTitle("Standrad Dialog Example");
    m_fileBtn = new QPushButton("File Standard Dialog");
    m_fileLineEdit = new QLineEdit;
    m_ColorBtn = new QPushButton("Color Dialog");
    m_ColorFrame = new QFrame;
    //设置边框风格
    m_ColorFrame->setFrameStyle(QFrame::Box);
    //设置填充背景属性
    m_ColorFrame->setAutoFillBackground(true);
    m_fontBtn = new QPushButton("Font Dialog");
    m_fontLineEdit = new QLineEdit("Test Font");
    m_inputBtn = new QPushButton("Standard Input Dialog");
    m_msgBtn = new QPushButton("Standard Message Box Dialog");
    m_customBtn = new QPushButton("Custom Message Box");
    m_customLabel = new QLabel;
    m_customLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
    m_mainLayout = new QGridLayout(this);
    m_mainLayout->addWidget(m_fileBtn,0,0);
    m_mainLayout->addWidget(m_fileLineEdit,0,1);
    m_mainLayout->addWidget(m_ColorBtn,1,0);
    m_mainLayout->addWidget(m_ColorFrame,1,1);
    m_mainLayout->addWidget(m_fontBtn,2,0);
    m_mainLayout->addWidget(m_fontLineEdit,2,1);
    m_mainLayout->addWidget(m_inputBtn,3,0,1,2);   //第三参数是占几行,第四参数是占几列
    m_mainLayout->addWidget(m_msgBtn,4,0,1,2);
    m_mainLayout->addWidget(m_customBtn,5,0);
    m_mainLayout->addWidget(m_customLabel,5,1);
    connect(m_fileBtn, &QPushButton::clicked,this,&Dialog::ShowFileDlg); // connect(sender, signal, receiver, slot);
                                                                         // sender:发出信号的对象。
                                                                         //signal:需要连接的信号,通常使用 &SenderClassName::signalName 形式表示。
                                                                         //receiver:接收信号并响应的对象,即槽函数所属的对象实例。
                                                                         //slot:当信号被发出时,应该被调用的函数,使用 &ReceiverClassName::slotName 形式表示。
    //connect(this,SIGNAL(Comeon(QString&)),this,SLOT(ComeonGuys(QString&))); <<<为什么这个写法没有 :: 呢? 因为 SIGNAL/SLOT 宏将它们转换为字符串。
                                                                                //这个字符串不需要包括函数所属的类名称,因为 SIGNAL 和 SLOT 宏已经处理了所有必要的信息,以便 Qt 的元对象系统可以解析它们。
    connect(m_ColorBtn,&QPushButton::clicked,this,&Dialog::ShowColorDlg);
    connect(m_fontBtn, &QPushButton::clicked,this,&Dialog::ShowFontDlg);
    connect(m_inputBtn, &QPushButton::clicked,this,&Dialog::ShowInputDlg);
    connect(m_msgBtn,&QPushButton::clicked, this,&Dialog::ShowMsgDlg);
    connect(m_customBtn, &QPushButton::clicked, this, &Dialog::ShowCustomDlg);
}
Dialog::~Dialog()
{
}
void Dialog::ShowFileDlg()
{
    QString sPath = QFileDialog::getOpenFileName(this,"File Standard Dialog",".",
                                 "C++ files(*.cpp);;C files(*.c;; Header files(*.h))");
    m_fileLineEdit->setText(sPath);
}
void Dialog::ShowColorDlg()
{
    QColor color = QColorDialog::getColor(Qt::red);    //red as default color // Not QColor::red
    if(color.isValid())
        m_ColorFrame->setPalette(QPalette(color));
}
void Dialog::ShowFontDlg()
{
    bool ok;
    QFont font= QFontDialog::getFont(&ok);      //参数位是否成功
    if(ok)
        m_fontLineEdit->setFont(font);
}
void Dialog::ShowInputDlg()
{
    m_inputDlg = new InputDlg(this);
    m_inputDlg->show();
}
void Dialog::ShowMsgDlg()
{
    m_msgboxDlg = new MsgBoxDialog(this);
    m_msgboxDlg->show();
}
void Dialog::ShowCustomDlg()
{
    m_customLabel->setText("Custom Message Dialog");
    QMessageBox* customMsgBox = new QMessageBox(this);
    customMsgBox->setWindowTitle("Custom Message Dialog");
    QPushButton* yes = customMsgBox->addButton("Yes",QMessageBox::ActionRole);  //ActionRole = 触发特定动作的按钮,这些动作与对话框的主要目的(如确认或取消)不直接相关。
    QPushButton* no = customMsgBox->addButton("No",QMessageBox::ActionRole);
    QPushButton* cancel = customMsgBox->addButton(QMessageBox::Cancel);
    customMsgBox->setIconPixmap(QPixmap("cat.gif"));
    customMsgBox->exec();
    if(customMsgBox->clickedButton() == yes)
        m_customLabel->setText("Clicked Yes");
    if(customMsgBox->clickedButton() == no)
        m_customLabel->setText("Clicked No");
    if(customMsgBox->clickedButton() == cancel)
        m_customLabel->setText("Clicked Cancel");
}
-  m_customLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);此setFrameStyle函数和带入参数是为了*- QFrame::Panel- 这个枚举值指定框架应该有一个面板风格,这意味着它会被渲染为一个凸起或凹陷的区域,具体取决于与之组合的另一个样式(如凹陷或凸起)
- QFrame::Sunken- 个枚举值表示框架应该呈现为凹陷风格,这在视觉上给人的感觉是框架边缘向内陷进去,使得框架内部的内容看起来像是低于周围表面的
 
-  另外,这里面使用了多次的 connect函数,大部分写法为connect(m_ColorBtn,&QPushButton::clicked,this,&Dialog::ShowColorDlg);-  为什么之前写的方法 connect(this,SIGNAL(Comeon(QString&)),this,SLOT(ComeonGuys(QString&)));第四个参数没有:: 符号?- 这是因为SLOT 宏将它们转换为字符串,从而执行该槽函数了
 
 
-  
-  另外,请注意QDialog类的对象要显示Dialog出来都需要 .show()或.exec()函数, 但QFileDialog, QMessageBox, QColorDialog, QFontDialog, QInputDialog对象不需要
-  QPushButton* yes = customMsgBox->addButton("Yes",QMessageBox::ActionRole);这里面 addButton函数的参数 ActionRole 为触发特定动作的按钮,这些动作与对话框的主要目的(如确认或取消)不直接相关。
当前项目添加C++类,类名InputDlg,基类QDialog
编辑inputdlg.h文件
#ifndef INPUTDLG_H
#define INPUTDLG_H
#include <QDialog>
#include <QLabel>
#include <QPushButton>
#include <QGridLayout>
class InputDlg : public QDialog
{
    Q_OBJECT
public:
    InputDlg(QWidget* parent = 0);
private:
    QLabel* m_nameTitle;    //Name:
    QLabel* m_sexTitle;     //Sex:
    QLabel* m_ageTitle;
    QLabel* m_scoreTitle;
    QLabel* m_nameLabel;    //
    QLabel* m_sexLabel;
    QLabel* m_ageLabel;
    QLabel* m_scoreLabel;
    QPushButton* m_nameBtn;
    QPushButton* m_sexBtn;
    QPushButton* m_ageBtn;
    QPushButton* m_scoreBtn;
    QGridLayout* m_mainLayout ;     //布局管理器
private slots:
    void editName();
    void editAge();
    void editSex();
    void editScore();
};
#endif // INPUTDLG_H
编辑InputDlg构造函数及添加相应槽函数的定义
#include "inputdlg.h"
#include <QInputDialog>
InputDlg::InputDlg(QWidget* parent):QDialog(parent)
{
    setWindowTitle("Input Dialog");
    m_nameTitle = new QLabel("Name:");
    m_sexTitle = new QLabel("Sex:");
    m_ageTitle = new QLabel("Age:");
    m_scoreTitle = new QLabel("Mark:");
    m_nameLabel = new QLabel("CCS");
    m_sexLabel = new QLabel("Male");
    m_ageLabel = new QLabel("27");
    m_scoreLabel = new QLabel("99.9");
    m_nameBtn = new QPushButton("Edit Name");
    m_sexBtn = new QPushButton("Edit Sex");
    m_ageBtn = new QPushButton("Edit Age");
    m_scoreBtn = new QPushButton("Edit Score");
    m_mainLayout = new QGridLayout(this);
    m_mainLayout->addWidget(m_nameTitle,0,0);
    m_mainLayout->addWidget(m_nameLabel,0,1);
    m_mainLayout->addWidget(m_nameBtn,0,2);
    m_mainLayout->addWidget(m_sexTitle,1,0);
    m_mainLayout->addWidget(m_sexLabel,1,1);
    m_mainLayout->addWidget(m_sexBtn,1,2);
    m_mainLayout->addWidget(m_ageTitle,2,0);
    m_mainLayout->addWidget(m_ageLabel,2,1);
    m_mainLayout->addWidget(m_ageBtn,2,2);
    m_mainLayout->addWidget(m_scoreTitle,3,0);
    m_mainLayout->addWidget(m_scoreLabel,3,1);
    m_mainLayout->addWidget(m_scoreBtn,3,2);
    m_mainLayout->setSpacing(20);
    m_mainLayout->setMargin(10);
    connect(m_nameBtn, &QPushButton::clicked,this,&InputDlg::editName);
    connect(m_sexBtn, &QPushButton::clicked,this,&InputDlg::editSex);
    connect(m_ageBtn, &QPushButton::clicked,this,&InputDlg::editAge);
    connect(m_scoreBtn, &QPushButton::clicked,this,&InputDlg::editScore);
}
void InputDlg::editName()
{
    bool ok;
    QString sName = QInputDialog::getText(this,"Standard Text Input","Please Edit Name",
                          QLineEdit::Normal,m_nameLabel->text(),&ok);
    if(ok)
        m_nameLabel->setText(sName);
}
void InputDlg::editAge()
{
    bool ok;
    int age = QInputDialog::getInt(this,"Standard int data type dialog","Please Edit Age",m_ageLabel->text().toInt(),0,120,1,&ok);  //why toInt, what is the parameter means
    if (ok)
        m_ageLabel->setText(QString("%1").arg(age));    //from int change to QString.
}
void InputDlg::editSex()
{
    bool ok;
    QStringList sexList;
    sexList<<"Male"<<"Female"<<"Non-stated";
    QString sex = QInputDialog::getItem(this,"Standard Selection Input","Please Select Sex:",
                          sexList,0,false,&ok);
    if(ok)
        m_sexLabel->setText(sex);
}
void InputDlg::editScore()
{
    bool ok;
    double score = QInputDialog::getDouble(this,"Standard double datatype input dialog",
                                           "Please input value",m_scoreLabel->text().toDouble(),
                                           0,100,2,&ok); // what is the parameter means for .toDouble
    if(ok)
        m_scoreLabel->setText(QString::number(score));  //number convert to QString
}
m_ageLabel->setText(QString("%1").arg(age)); 
正则表达式
正则表达式即一个文本匹配字符串的一种模式,Qt中QRegExp类实现使用正则表达式进行模式匹配,且完全支持Unicode,主要应用:字符串验证、搜索、查找替换、分割。
正则表达式中字符及字符集
| 元素 | 含义 | 
|---|---|
| c | 匹配字符本身,如a匹配a | 
| \c | 跟在\后面的字符匹配字符本身,但本表中下面指定的这些字符除外。 | 
| \a | 匹配ASCII的振铃 | 
| \f | 匹配ASCII的换页 | 
| \n | 匹配ASCII的换行 | 
| \r | 匹配ASCII的回车 | 
| \t | 匹配ASCII的水平制表符 | 
| \v | 匹配ASCII的垂直制表符 | 
| \xhhhh | 匹配Unicode字符对应的十六进制数 | 
| \0ooo | 匹配八进制的ASCII/Latin1字符 | 
| . | 匹配任意字符 | 
| \d | 匹配任意一个数字 | 
| \D | 匹配一个非数字 | 
| \s | 匹配一个空白字符,包括“\t”、“\n”、“\v”、“\f”、“\r”及“” | 
| \S | 匹配一个非空白字符 | 
| \w | 匹配一个单词字符,包括任意字符数字下划线,即AZ,az,0~9中任意一个 | 
| \W | 匹配一个非单词字符 | 
| \n | 第n个反向引用 | 
正则表达式中的量词
| 量词 | 含义 | 
|---|---|
| E? | 匹配0次或1次等价于E{0,1} | 
| E+ | 匹配1次或多次,等价于E{1,} | 
| E* | 匹配0次或多次,等价于E{0,} | 
| E{n} | 匹配n次 | 
| E{n,} | 匹配至少n次 | 
| E{,m} | 匹配至多m次 | 
| E{n,m} | 匹配至少n次,至多m次 | 
正则表达式中的断言
| 断言 | 含义 | 
|---|---|
| ^ | 标志字符串的开始。若匹配“”则使用“\” | 
| $ | 标志字符串的结尾。若匹配“$”则使用“\$” | 
| \b | 一个单词的边界 | 
| \B | 一个非单词的边界。当\b为false则它为true | 
| (?=E) | 表达式后紧跟E才匹配 | 
| (?!E) | 表达式后不跟E才匹配 | 
QRegExp同时支持通配符
| 通配符 | 含义 | 
|---|---|
| c | 任意一个字符,表字符本身 | 
| ? | 任意一个字符,类似regexp中“.” | 
| * | 任意0个或多个字符 | 
| […] | 在[]中的字符集 | 
新建控制台应用程序,项目名QRegExp
main.cpp
#include <QCoreApplication>
#include <QDebug>
#include <QRegularExpression>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QRegExp reg("a");
    qDebug() <<"Match Char 'abc':"<<reg.exactMatch("abc");
    qDebug() <<"Match char 'a':"<<reg.exactMatch("a");
    QRegExp reg0("(\\d*\\D{2})");
    qDebug()<<"Match Num:"<<reg0.exactMatch("183cm")<<reg0.exactMatch("183m")<<reg0.exactMatch("72in");
    QRegExp rx("*.txt");
    rx.setPatternSyntax(QRegExp::Wildcard); //支持通配符 - 设置匹配语法
    qDebug()<<"Wildcard Match:"<<rx.exactMatch("License.txt")<<rx.exactMatch("License.txt.bak");
    //匹配单词边界
    QRegExp reg1;
    reg1.setPattern("\\b(hello|Hello)\\b");     // \\b可以帮助确保你的正则表达式匹配完整的单词而不是单词的一部分
    qDebug()<<"Match Muiltiple Word:"<<reg1.indexIn("Hi,helloEveryOne") //-1 没找到
           <<reg1.indexIn("Hmmm hello everyone")                        //5 在第5个位置
          <<reg1.indexIn("Hi Boys, Hello girls");                       //9  在第9个位置
    //捕获匹配文本
    //由"(?:"开始, ")" 结束
    QRegExp regHeight("(\\d+)(?:\\s*)(cm|inch)");
    int res = regHeight.indexIn("YaoMing 226cm");
    if(res > -1)
    {
        qDebug()<<"The text captured:"<<"Cap(0):"<<regHeight.cap(0)
               <<"cap(1):"<<regHeight.cap(1)
              <<"cap(2):"<<regHeight.cap(2);
    }
    //断言?! 不紧跟才算匹配.  ?= 紧跟才匹配
    QRegExp reg2;
    reg2.setPattern("Noodle(?!s)");
    //reg2.setCaseSensitivity(Qt::CaseInsensitive); 如果需要设置成大小写不敏感
    QString str = "Noodle is sold out, but Noodles have! Noodles is the best!";
    qDebug()<<str<<endl;
    str.replace(reg2,"Bread");
    qDebug()<<str<<endl;
    //QT5 引入新的类
    QRegularExpression regExp("hello");
    qDebug()<<"QRegularExpression Match Character:"<<regExp.match("hello world!");
    regExp.setPattern("^[A-Z]{3,8}$");  //设置匹配模式
    regExp.setPatternOptions(QRegularExpression::CaseInsensitiveOption);
    qDebug()<<"Case Insensitive Match:"<<regExp.match("hello");
    QRegularExpression reDate("^(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)$");
    QRegularExpressionMatch match0 = reDate.match("01/10/1949");
    if(match0.hasMatch())
    {
        QString strMatch = match0.captured(0);
        QString day = match0.captured(1);
        QString month = match0.captured(2);
        QString year = match0.captured(3);
        qDebug()<<"QRegularExpression Captured Text: "<<"strMatch:"<<strMatch
                <<"day:"<<day
                <<"month:"<<month
                <<"year:"<<year;
    }
    //部分匹配
    QString sPattern;
    sPattern = "^(Jan|Feb|March|April|May|June) \\d\\d \\d\\d\\d\\d$";
    QRegularExpression reDate1(sPattern);
    QString ss("Feb 28");
    QRegularExpressionMatch match2;
    match2 = reDate1.match(ss,0,QRegularExpression::PartialPreferCompleteMatch);
    bool bHasMatched = match2.hasMatch();
    bool bPartial = match2.hasPartialMatch();
    qDebug()<<bHasMatched <<bPartial;
    return a.exec();
}
-  当在使用正则表达式并且该表达式包含反斜杠 \时,需要使用两个反斜杠\\来表达一个单独的反斜杠
-  rx.setPatternSyntax(QRegExp::Wildcard);setPatternSyntax 的函数用于设置用于解析正则表达式的语法模式. 代码rx.setPatternSyntax(QRegExp::Wildcard);的意思是将QRegExp对象rx的解析模式设置为Wildcard
-  IndexIn函数用于在字符串中查找与正则表达式匹配的第一个位置
-  .cap()方法用于返回由正则表达式匹配的特定捕获组的内容
-  \b单词的边界 意思是单词边界可以帮助确保你的正则表达式匹配完整的单词而不是单词的一部分
-  QRegularExpression是QT5引入的新类,需要包括头文件#include <QRegularExpression>>
-  QRegExp和QRegExpression设置大小写不敏感匹配方法不一样,QRegExp是使用方法setCaseSensitivity而QRegularExpression是使用setPatternOptions(QRegularExpression::CaseInsensitiveOption)方法
-  QRegularExpressionMatch类用于表示由QRegularExpression对象进行模式匹配操作后的结果。QRegularExpressionMatch` 类提供了多种方法来检索有关匹配的信息,例如: - 是否匹配成功:可以通过调用 hasMatch()方法来检查是否存在至少一个匹配。
- 是否部分匹配成功**:hasPartialMatch()方法用于检查是否存在部分匹配,这在某些高级匹配情况下有用。
- 捕获组的内容:可以使用 captured(int)方法获取具体的捕获组的文本。这里的参数是捕获组的索引,其中0索引代表整个匹配的文本,1和更高的索引代表具体的子捕获组。**
- 捕获组的开始和结束位置:通过 capturedStart(int)和capturedEnd(int)方法可以获取任意捕获组的开始和结束位置。
- 捕获组的数量:使用 capturedTexts()或lastCapturedIndex()方法可以获得匹配中包含的捕获组数量。
 
- 是否匹配成功:可以通过调用 
-  QRegularExpression部分匹配需写成QRegularExpressionMatch match2 = reDate1.match(ss,0,QRegularExpression::PartialPreferCompleteMatch);
-  QRegExp不直接支持像QRegularExpression那样的部分匹配模式选项.- 所以类似部分匹配需写成"^(Jan|Feb|March|April|May|June) \\d\\d(?: \\d\\d\\d\\d)?$" 这样的话无论有没有输入年份都可以匹配了.
 
- 所以类似部分匹配需写成
文件处理
QFile类用于文件操作,它提供了读写文件的接口,可以读写文件、二进制文件和Qt资源文件。
处理文本文件和二进制文件,可以使用QTextStream类和QDataStream类。处理临时文件可以使用QTemporaryFile,获取信息可以使用QFileInfo,处理目录可以使用QDir,监视文件和目录变化可以使用QFileSystemWatcher
QTextStream的流操作符
| 操作符 | 作用描述 | 
|---|---|
| bin | 设置读写的整数为 二进制数 | 
| oct | 设置读写的整数为 八进制数 | 
| dec | 设置读写的整数为十进制数 | 
| hex | 设置读写的整数为十六进制数 | 
| showbase | 强制显示进制前缀,如十六进制(0x)、八进制(0)、二进制(0b) | 
| forcesign | 强制显示符号(+、-) | 
| forcepoint | 强制显示小数点 | 
| noshowbase | 强制不显示前缀 | 
| noforcesign | 强制不显示符号 | 
| uppercasebase | 显示大写的进制前缀 | 
| lowercasebase | 显示小些的进制前缀 | 
| uppercasedigits | 用大写字母表示 | 
| lowercasedigits | 用小写字母表示 | 
| fixed | 固定小数点表示 | 
| scientific | 科学计数法表示 | 
| left | 左对齐 | 
| right | 右对齐 | 
| center | 居中 | 
| endl | 换行 | 
| flush | 清除缓冲 | 
QFile QTextStream操作文件示例:
新建控制台引用程序,编辑main函数,main.cpp添加头文件:
#include <QCoreApplication>
#include <QFile>
#include <qDebug>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QFile file("hello.txt");
    //读取文件
    if(file.open(QIODevice::ReadOnly))     //参数是带入QIODevice的枚举类型, 因为点开open函数的话,他是需要OpenMode 类型
                                           //然而OpenMode 类型是通过Q_DECLARE_FLAGS<新函数名,枚举类型>函数创建的新类型,其枚举类型是
                                           //QIODevice 的枚举类型,因此此处带入QIODevice没问题.
    {
        char buffer[100];
        qint32 n;               //等于int类型
        n = file.readLine(buffer,sizeof(buffer));    //.readLine函数若返回-1,表示读取失败
                                                    //若成功读取则返回读取的字节数
        if(n!=-1)
        {
          qDebug()<<"Sucessfully read the file";
          qDebug()<<"Length:"<<n
                    <<"buffer:"<<buffer;
          file.close();                             //一定要记得关闭文件
        }
        else
        {
            qDebug()<< file.errorString();      //会返回QIODevice(QFile的父类)的成员的错误原因
        }
        //文件写入
        double dPI = 3.1415926;
        int age = 13;
        QFile dataFile;
        dataFile.setFileName("data.txt");   //设置 QFile类对象的文档名字
        if(dataFile.open(QFile::WriteOnly | QFile::Truncate))           //和dataFile.open(QIODevice::WriteOnly | QIODevice::Truncate) 写法一样
                                                                        //因为QFile是作为QIODevice的子类,继承了枚举值
        {
            QTextStream out(&dataFile);     //带入参数为QIODevice类型,QFile类型也可以.
            out.setRealNumberPrecision(3);
            out.setFieldWidth(10);
            out.setFieldAlignment(QTextStream::AlignRight);
            //文件流操作
            out<<QString("PI:")
//               <<qSetRealNumberPrecision(3) //需设置精度,否则默认为6位
//               <<qSetFieldWidth(10)         //文件内容必须为10位宽,否则会加入多个空行,默认右对齐
               <<scientific
               <<left                       //设置成右对齐
               <<dPI;
//              <<hex
//              <<showbase
//              <<uppercasebase
//              <<age;
        }
    }
    return a.exec();
}
-  open参数是带入QIODevice或QFile的, 其参数是枚举值,但为什么点开Open函数的话带入的类型是OpenMode类型的呢?原因是 openMode类型是通过Q_DECLARE_FLAGS<新函数名,枚举类型>函数创建的新类型,其枚举类型是QIODevice的枚举类型,因此此处带入QIODevice没问题,QFile也没问题是因为QFile是QIODevice的子类
-  qint32类型也等于int类型
-  readLine第一参数为读取文档内容后放置的变量,第二参数为读取最大限制字符大小.返回值为读取字符大小,若返回-1表示读取数据失败
-  setFileName函数用于设置 QFile类对象的文档名字
-  QTextStream out(&dataFile);带入参数为QIODevice类型,QFile类型也可以.
-  上表格的文件流的操作是基于类似 out<<QString("PI:")<<qSetRealNumberPrecision(3) <<scientific<<dPI的写法- 然而,QTextStream也有基于成员函数的写法,比如setRealNumberPrecision(3),setFieldWidth(10)
- 为什么有两种不同的写法呢? 原因是一种是基于成员函数设置, 另一种是基于操纵符的设置
 示例代码差异 out.setRealNumberPrecision(3); out.setFieldWidth(10); out.setFieldAlignment(QTextStream::AlignRight); out << scientific << left << dPI;
- 然而,
读写二进制文件示例:
新建控制台应用程序,编辑main函数,main.cpp添加头文件
#include <QCoreApplication>
#include <QFile>
#include <QDate>
#include <QDataStream>
#include <QDebug>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QFile binFile("info.bat");
    if(binFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
    {
        //往文件中写入数据
        QDataStream out(&binFile);
        out<<QString("Micheal Jackson")
           <<QDate::fromString("1950-11-29","yyyy-MM-dd")
           <<(qint32)19;
        binFile.close();
    }
    else
    {
        qDebug()<<binFile.errorString();
    }
    //binFile.setFileName("info.bat");    //可以不执行这一行,但是如果前面没有 into.txt的话只是读取一个外部的文件的话就需要设置.
    
    if(binFile.open(QIODevice::ReadOnly))
    {
        QDataStream in(&binFile);
        QString name;
        QDate birthday;
        qint32 age;
        in>>name>>birthday>>age;
        qDebug()<<name<<birthday<<age;
        binFile.close();
    }
    return a.exec();
}
- 上面代码的binFile.setFileName("info.bat")可以不写,但是如果情况是上面没有定义过 binFile的文档名的话就需要,不然程序都不知道要对哪个文档执行操作
- QDate::fromString("1950-11-29","yyyy-MM-dd")第一参数为日期,第二参数为日期格式
QFileInfo类获取文件信息示例:
新建桌面应用程序,基类QWidget,类名FileInfo,勾选创建界面文件
设计模式下设计界面

fileinfo.h文件添加成员函数及槽函数声明:
#ifndef FILEINFO_H
#define FILEINFO_H
#include <QWidget>
namespace Ui {
class FileInfo;
}
class FileInfo : public QWidget
{
    Q_OBJECT
public:
    explicit FileInfo(QWidget *parent = 0);
    ~FileInfo();
    void getFileInfo(QString& fileName);
private slots:
    void on_browseFileBtn_clicked();
private:
    Ui::FileInfo *ui;
};
#endif // FILEINFO_H
fileinfo.cpp编辑构造函数并添加成员函数定义:
#include "fileinfo.h"
#include "ui_fileinfo.h"
#include <QFileDialog>
#include <QDateTime>
FileInfo::FileInfo(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::FileInfo)
{
    ui->setupUi(this);
    setWindowTitle("File Information");
}
FileInfo::~FileInfo()
{
    delete ui;
}
void FileInfo::getFileInfo(QString &fileName)
{
    QFileInfo info(fileName);
    qint64 size = info.size();  //.size函数返回qint64类型.
    QDateTime createTime = info.created();  //文件创建时间
    QDateTime lastMTime = info.lastModified();  //文件修改时间
    QDateTime lastRTime = info.lastRead();  //文件访问时间
    //文件的属性信息
    bool bIsDir = info.isDir();
    bool bIsFile = info.isFile();
    bool bIsSymLink = info.isSymLink();
    bool bIsHidden = info.isHidden();
    bool bIsReadable = info.isReadable();
    bool bIsWriteable = info.isWritable();
    bool bIsExutable = info.isExecutable();
    ui->fileSizeLineEdit->setText(QString::number(size));       //数字转QString
    ui->fileCreatedLineEdit->setText(createTime.toString());    //日期转QString
    ui->fileModifiedLineEdit->setText(lastMTime.toString());
    ui->fileVisitedLineEdit->setText(lastRTime.toString());
    ui->isDirCheckBox->setChecked(bIsDir);
    ui->isFileCheckBox->setChecked(bIsFile);
    ui->IsSymLnkCheckBox->setChecked(bIsSymLink);
    ui->isHideCheckBox->setChecked(bIsHidden);
    ui->isWriteCheckBox->setChecked(bIsWriteable);
    ui->isReadCheckBox->setChecked(bIsReadable);
    ui->isExecuteCheckBox->setChecked(bIsExutable);
}
void FileInfo::on_browseFileBtn_clicked()
{
    QString fileName = QFileDialog::getOpenFileName(this,"Open File...",".","files(*)");
    //QString path = QFileDialog::getExistingDirectory(this,"Select Directory", ".");   //选择目录方法
    ui->fileNameLineEdit->setText(fileName);
    getFileInfo(fileName);
}
我的WPS项目
新建项目
新建WPS,基类QMainWindow,勾选界面文件
菜单项工具栏


菜单属性

新建子窗口
项目添加ChildWnd类,继承自QTextEdit
ChildWnd.h
#ifndef CHILDWND_H
#define CHILDWND_H
#include <QTextEdit>
class ChildWnd : public QTextEdit
{
    Q_OBJECT
public:
    ChildWnd();
    QString m_CurDocPath;   //当前文档路径
    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_CurDocPath = QString("WPS 文档 %1").arg(wndSeqNum++);
    //设置窗体标题,文档改动后名称加'*'号标识
    setWindowTitle(m_CurDocPath+"[*]"+ "- MyWPS");
    connect(document(),SIGNAL(contentsChanged()),this,SLOT(docBeModified()));       //contentsChanged是QTextDocument对象发出的信号,而不是QTextEdit.所以此处第一参数是documwnt() (返回QTextDocument当前对象的指针)
}
QString ChildWnd::getCurDocName()
{
    return QFileInfo(m_CurDocPath).fileName();
}
void ChildWnd::docBeModified()
{
    setWindowModified(document()->isModified());    //setWindowModified通常用在QWidget类,这个方法用来指示窗口的内容自上次保存以来是否已被修改
}
- setAttribute方法用于设置窗口或对话框关闭时自动删除对象,释放内存。这是一种管理内存的便捷方式,特别是在处理多个窗口或对话框时非常有用
- contentsChanged槽方法是- QTextDocument对象发出的信号,而不是- QTextEdit.所以此处第一参数是- document()(返回- QTextDocument当前对象的指针)
- setWindowModified方法通常用在QWidget类,这个方法用来指示窗口的内容自上次保存以来是否已被修改
- 疑问:为什么上述代码setAttribute&setWindowModified方法无需this->呢?这是因为有隐藏式的使用this指针
Mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
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();
private:
    void formatEnabled();
private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <childwnd.h>
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())
        ui->textSizeComboBox->addItem(QString::number(fontsize));
    QFont defFont;          //当前应用程序默认字体
    QString sFontSize;
    int defFontSize;        //当前应用程序默认字体字号
    int defFontindex;       //当前字号在组合框中的索引号
    defFont = QApplication::font();
    defFontSize = defFont.pointSize();
    sFontSize = QString::number(defFontSize);
    defFontindex = ui->textSizeComboBox->findText(sFontSize);
    ui->textSizeComboBox->setCurrentIndex(defFontindex);
}
void MainWindow::docNew()
{
    ChildWnd *childwnd = new ChildWnd;
    ui->mdiArea->addSubWindow(childwnd);
    connect(childwnd,SIGNAL(copyAvailable(bool)),ui->Cut_Action,SLOT(setEnabled(bool)));        //childwnd对象会发出copyAvailable的信号,而此信号的bool会决定setEnabled的bool是true/false.
    connect(childwnd,SIGNAL(copyAvailable(bool)),ui->Copy_Action,SLOT(setEnabled(bool)));       //写成connect(childwnd, &ChildWnd::copyAvailable, ui->Copy_Action, &QAction::setEnabled);
    childwnd->newDoc();
    childwnd->show();
    formatEnabled();
}
void MainWindow::formatEnabled()        //启用一组与文本格式化相关的操作
{   
    ui->BoldAction->setEnabled(true);       //setEnabled在Qt中同时是一个普通的成员函数和一个可以用作槽的函数
    ui->ItalicAction->setEnabled(true);     //QT里面定义的public slot槽方法可以作为响应信号的槽,也可以作为普通的类方法被调用
    ui->Underline_Action->setEnabled(true);
    ui->leftAlignAction->setEnabled(true);
    ui->CentreAction->setEnabled(true);
    ui->RightAction->setEnabled(true);
    ui->justifyAction->setEnabled(true);
    ui->ColorAction->setEnabled(true);
}
void MainWindow::on_newAction_triggered()
{
    docNew();
}
- connect(childwnd,SIGNAL(copyAvailable(bool)),ui->Cut_Action,SLOT(setEnabled(bool)));childwnd对象会发出copyAvailable的信号,而此信号的bool会决定setEnabled的bool是true/false.
- 上面代码也可写成connect(childwnd, &ChildWnd::copyAvailable, ui->Copy_Action, &QAction::setEnabled);
子窗口管理
设置图标,.pro文件添加RC_ICONS += images/wps.ico
QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
RC_ICONS += images/wps.ico
设置窗体标题

Mainwindow.h
``````
private slots:
    void on_newAction_triggered();
    void refreshMenus();
    void addSubWndListMenu();
    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 setActiveSubWindow(QWidget* wnd);
protected:
    void closeEvent(QCloseEvent *event);
private:
    void formatEnabled();
    ChildWnd *activateChildWnd();
private:
    Ui::MainWindow *ui;
    QSignalMapper* m_WndMapper;     //信号映射器
};
#endif // MAINWINDOW_H
MainWindow.cpp
#include <childwnd.h>
#include <QMdiSubWindow>
#include <QCloseEvent>
``````
    
void MainWindow::initMainWindow()
{
	``````
    ui->textSizeComboBox->setCurrentIndex(defFontindex);
    //设置多文档滚动条
    ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    refreshMenus();
    connect(ui->mdiArea,&QMdiArea::subWindowActivated,this,&MainWindow::refreshMenus);  //信号是当用户激活(选择或切换)到其中一个子窗口时,调用槽函数
    addSubWndListMenu();
    connect(ui->menu_W,&QMenu::aboutToShow,this,&MainWindow::addSubWndListMenu);    //在ui界面右键'窗体'选项就可以看到该选项名字为'menu_W'
                                                                                    //点击窗体后就会发射信号,addSubWndListMenu槽函数会被调用
    //创建信号映射器
    m_WndMapper = new QSignalMapper(this);		//信号映射器
    connect(m_WndMapper,SIGNAL(mapped(QWidget*)),this,SLOT(setActiveSubWindow(QWidget*)));
}
void MainWindow::on_newAction_triggered()
{
    docNew();
}
void MainWindow::refreshMenus()
{
    bool hasChild = false;
    hasChild = (activateChildWnd() != 0);
    ui->SaveAction->setEnabled(hasChild);
    ui->SaveAsAction->setEnabled(hasChild);
    ui->PrintAction->setEnabled(hasChild);
    ui->PrintPreAction->setEnabled(hasChild);
    ui->Paste_Action->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());
    ui->Cut_Action->setEnabled(hasSelect);
    ui->Copy_Action->setEnabled(hasSelect);
    ui->BoldAction->setEnabled(hasSelect);
    ui->ItalicAction->setEnabled(hasSelect);
    ui->Underline_Action->setEnabled(hasSelect);
    ui->leftAlignAction->setEnabled(hasSelect);
    ui->RightAction->setEnabled(hasSelect);
    ui->justifyAction->setEnabled(hasSelect);
    ui->ColorAction->setEnabled(hasSelect);
}
void MainWindow::addSubWndListMenu()
{
    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();      //把所有的subWindow都加进QList容器里
    if(!wnds.isEmpty()) ui->menu_W->addSeparator();					//menu_W在ui界面右键可以查看当前menu名称,然后对这个menu_W菜单加上separator分割窗
    for(int i =0; i<wnds.size(); i++)
    {
        ChildWnd* childwnd = qobject_cast<ChildWnd*>(wnds.at(i)->widget());	//类型转换
        QString menuitem_text;
        menuitem_text = QString("%1 %2").arg(i+1).arg(childwnd->getCurDocName());
        QAction *menuitem_act = ui->menu_W->addAction(menuitem_text);
        menuitem_act->setCheckable(true);
        menuitem_act->setChecked(childwnd == activateChildWnd());
        connect(menuitem_act,SIGNAL(triggered(bool)),m_WndMapper,SLOT(map()));
        m_WndMapper->setMapping(menuitem_act,wnds.at(i));      
        //第一参数为信号源对象,第二参数为希望传递给 QSignalMapper 的 mapped() 信号的参数
        //信号映射器通常需要两个connect函数,
        //第一个 connect: 连接不同的信号源到 QSignalMapper 的 map() 槽。
        //第二个 connect: 连接 QSignalMapper 的 mapped() 信号到实际的槽函数。
    }
    formatEnabled();
}
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();
}
void MainWindow::on_nextAction_triggered()
{
    ui->mdiArea->activateNextSubWindow();
}
void MainWindow::on_previousAction_triggered()
{
    ui->mdiArea->activatePreviousSubWindow();
}
void MainWindow::setActiveSubWindow(QWidget *wnd)
{
    if(!wnd) return;
    ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(wnd));
}
void MainWindow::closeEvent(QCloseEvent *event)
{
    ui->mdiArea->closeAllSubWindows();
    if(ui->mdiArea->currentSubWindow())
        event->ignore();    //忽略此事件
    else
        event->accept();    //接受此事件
}
-  bool hasSelect = (activateChildWnd() && activateChildWnd()->textCursor().hasSelection());这行代码表示 文档打开且有内容选中
-  ChildWnd* childwnd = qobject_cast<ChildWnd*>(wnds.at(i)->widget());这个代码表示了类型转换, 其中wnds是装有多个QMdiSubWindow的容器,而实际的子窗口是通过widget()函数返回的,这个函数返回的是一个指向QWidget的指针
-  qobject_cast可以直接将指针转换为目标类型,只要目标类型是源类型的子类或派生类,无需进行中间转换。
-  信号映射器 QSignalMapper通常需要两个connect函数,-  第一个 connect: 连接不同的信号源到QSignalMapper的map()槽。connect(menuitem_act,SIGNAL(triggered(bool)),m_WndMapper,SLOT(map()));
-  第二个 connect: 连接 QSignalMapper 的 mapped() 信号到实际的槽函数。 connect(m_WndMapper,SIGNAL(mapped(QWidget*)),this,SLOT(setActiveSubWindow(QWidget*)));
 
-  
-  通常需要使用信号映射器时,需要new 一个 QSignalMapper对象,并且在谅解不同信号到QSignalmapper的Map槽函数时需要- m_WndMapper->setMapping(menuitem_act,wnds.at(i));第一参数为信号源对象,第二参数为希望传递给 QSignalMapper 的 mapped() 信号的参数
 
-  void MainWindow::closeEvent(QCloseEvent *event)中的 QCloseEvent类是继承自QEvent,并且是一个特定类型的事件对象,当一个窗口(如QWidget或其子类)即将关闭时,Qt会发出这个事件
打开文档
ChildWnd.h添加声明
``````
    public:
    bool loadDoc(const QString& docName);
    void setCurDoc(const QString& docName);
ChildWnd.cpp添加定义
bool ChildWnd::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()));
    }
}
void ChildWnd::setCurDoc(const QString &docName)
{
    //canonicalFilePath()返回标准名称路径,可过滤"."
    m_CurDocPath = QFileInfo(docName).canonicalFilePath();
    m_bSaved = true;                        //文档已被保存
    document()->setModified(false);           //文档未改动
    setWindowModified(false);               //窗口不显示改动标识
    setWindowTitle(getCurDocName() + "[*]");  //设置子窗口标题
}
- file.open()方法用于打开文件。这个方法的参数是一个枚举值- QIODevice::OpenModeFlag,用于指定打开文件的模式- 里面可以带入ReadOnly,WriteOnly,ReadWrite枚举值
 
- 里面可以带入
- QByteArray text = file.readAll();此行代码- readAll()方法会返回一个- QByteArray参数.其中- QByteArray可以存储和表示任何二进制数据,这使得它可以用于读取任意类型的文件内容,包括文本文件和二进制文件
- if(Qt::mightBeRichText(text))此Qt静态函数若点开的话该带入一个- QString参数,但为什么上面代码带入- QByteArray也可以呢?- 这是因为Qt 提供了灵活的转换机制,使得某些函数可以接受不同但相关的参数类型。这是通过隐式构造函数实现的
- QString(const QByteArray &);这个构造函数可以让 QByteArray 隐式地转换为 QString。因此,当一个函数接受 QString 作为参数时,你可以传入一个 QByteArray,它会自动转换为 QString。
 
- setHtml和- setPlainText方法源自于- QTextEdit,也就是- ChildWnd的基类
- m_CurDocPath = QFileInfo(docName).canonicalFilePath();docName是一个QString类型参数,需要带入进- QFileInfo来使- QFileInfo的构造函数创建一个- QFileInfo对象然后再调用- canonicalFilePath()方法返回一个文件的规范路径 (消除了路径中的冗余部分(如- .和- ..等))
- QTextEdit::document()返回的- QTextDocument对象是与该- QTextEdit小部件关联的文本文档对象。每个- QTextEdit实例都有一个独立的- QTextDocument对象,用于存储和管理该编辑器中的文本内容及其格式化信息。
Mainwindow.h
``````
public:
void docOpen();
````````
    
private:
QMdiSubWindow *findChildWnd(const QString& docName);
```````
    
private slots:
void on_OpenAction_triggered();
MainWindow.cpp
#include <QFileDialog>
void MainWindow::docOpen()
{
    QString docName = QFileDialog::getOpenFileName(this,"Open the file","","Text Document(*.txt);;HTML File(*.html *.htm);;" "All Files(*.*)");
    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->Cut_Action,SLOT(setEnabled(bool)));
        connect(childWnd,SIGNAL(copyAvailable(bool)),ui->Copy_Action,SLOT(setEnabled(bool)));
        if(childWnd->loadDoc(docName))
        {
            statusBar()->showMessage("Document is opened",3000);
            childWnd->show();
            formatEnabled();
        }
        else
        {
            childWnd->close();
        }
    }
}
QMdiSubWindow *MainWindow::findChildWnd(const QString &docName)
{
    QString strFile = QFileInfo(docName).canonicalFilePath();
    foreach(QMdiSubWindow* subWnd, ui->mdiArea->subWindowList())
    {
        ChildWnd* childWnd = qobject_cast<ChildWnd*>(subWnd->widget());
        if(childWnd->m_CurDocPath == strFile) return subWnd;
    }
    return 0;
}
void MainWindow::on_OpenAction_triggered()
{
    docOpen();
}
- QString docName = QFileDialog::getOpenFileName(this,"Open the file","","Text Document(*.txt);;HTML File(*.html *.htm);;" "All Files(*.*)");这个文档对话框过滤文档类型时需使用符号- ;;分隔不同的文档类型
- statusBar()->showMessage("Document is opened",3000);- statusBar是- QMainWindow自带的方法,- QMainWindow提供了一个默认的状态栏,您可以通过- statusBar()方法访问
文档保存
ChildWnd.h添加声明
``````
public:
bool saveDoc();
bool saveAsDoc();
bool saveDocOpt(QString docName);
private:
bool promptSave();
ChildWnd.cpp添加定义
bool ChildWnd::saveDoc()
{
    if(m_bSaved) return saveDocOpt(m_CurDocPath);
    else saveAsDoc();
}
bool ChildWnd::saveAsDoc()
{
    QString docName = QFileDialog::getSaveFileName(this,
                                 "Save as...",
                                 m_CurDocPath,
                                 "HTML doc (*.html);;"
                                 "All files(*.*)");
    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());
    if(isSuccess) setCurDoc(docName);
    return isSuccess;
}
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,"System Hint",QString("Your document%1 changed, do you want to save?").arg(getCurDocName()),
                                  (QMessageBox::Yes | QMessageBox:: Discard | QMessageBox::No));
     if (result == QMessageBox::Yes)
         return saveDoc();
     else if(result == QMessageBox::No)
         return false;
     return true;
}
- QTextDocumentWrite类 是 Qt 框架中用于将- QTextDocument(文本文档)对象保存到外部文件的类。
- write方法用于将- QTextDocument对象的数据写入到指定的文件中- 通常使用方法是先声明一个QTextDocumentWriter对象,然后再对该对象调用write方法保存文档
 
- 通常使用方法是先声明一个
MainWindow.h添加声明:
``````
public:
    void docSave();
    void docSaveAs();
``````
private slots:
    void on_SaveAction_triggered();
    void on_SaveAsAction_triggered();
MainWindow.cpp添加定义
void MainWindow::docSave()
{
    if(activateChildWnd() && activateChildWnd()->saveDoc())
    {
        statusBar()->showMessage("Save completed", 3000);
    }
}
void MainWindow::docSaveAs()
{
    if(activateChildWnd() && activateChildWnd()->saveAsDoc())
    {
      statusBar()->showMessage("Sucessfully save",3000);
    }
}
void MainWindow::on_SaveAction_triggered()
{
    docSave();
}
void MainWindow::on_SaveAsAction_triggered()
{
    docSaveAs();
}
文档操作
MainWindow.h添加声明
``````
public:
    void docUndo();
    void docRedo();
    void docPaste();
    void docCut();
    void docCopy();
``````
private slots:
    void on_UndoAction_triggered();
    void on_Redo_Action_triggered();
    void on_Cut_Action_triggered();
    void on_Copy_Action_triggered();
    void on_Paste_Action_triggered();
MainWindow.cpp添加定义
void MainWindow::docUndo()
{
    if(activateChildWnd())
        activateChildWnd()->undo();
}
void MainWindow::docRedo()
{
    if(activateChildWnd())
        activateChildWnd()->redo();
}
void MainWindow::docPaste()
{
    if(activateChildWnd())
        activateChildWnd()->paste();
}
void MainWindow::docCut()
{
    if(activateChildWnd())
        activateChildWnd()->cut();
}
void MainWindow::docCopy()
{
    if(activateChildWnd())
        activateChildWnd()->copy();
}
void MainWindow::on_UndoAction_triggered()
{
    docUndo();
}
void MainWindow::on_Redo_Action_triggered()
{
    docRedo();
}
void MainWindow::on_Cut_Action_triggered()
{
    docCut();
}
void MainWindow::on_Copy_Action_triggered()
{
    docCopy();
}
void MainWindow::on_Paste_Action_triggered()
{
    docPaste();
}
字体格式
设置加粗、倾斜、下划线菜单项可选属性。

ChildWnd.h添加声明
``````
public:
void setFormatOnSelectedWord(const QTextCharFormat &fmt);
ChildWnd.cpp添加定义
``````
void ChildWnd::setFormatOnSelectedWord(const QTextCharFormat &fmt)
{
    QTextCursor tCursor = textCursor();
    if(tCursor.hasSelection()) tCursor.select(QTextCursor::WordUnderCursor);
    tCursor.mergeCharFormat(fmt);
    mergeCurrentCharFormat(fmt);
}
- textCursor方法返回一个- QTextCursor对象,表示文本编辑器中的当前光标位置或选择
- itCursor.hasSelection()用于检查- QTextCursor对象- tCursor是否有选择范围
- tCursor.select(QTextCursor::WordUnderCursor)其中- select用于选择特定范围的文本- QTextCursor::WordUnderCursor是一个枚举值,表示光标下的整个单词
- megeCharFormat(fmt)方法将传入的- QTextCharFormat格式- fmt应用于当前光标(或选择的文本)
- mergeCurrentCharFormat(fmt);用于将指定的字符格式- fmt应用于当前的字符格式。它会影响后续的文本输入,即光标移动到其他地方时,新的文本会使用合并后的字符格式
MainWindow.h添加声明
``````
public:
    void textBold();
    void textItalic();
    void textUnderline();
private slots:
    void on_BoldAction_triggered();
    void on_ItalicAction_triggered();
    void on_Underline_Action_triggered();
MainWindow.cpp添加定义
``````
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->Underline_Action->isChecked());
    if(activateChildWnd())
        activateChildWnd()->setFormatOnSelectedWord(fmt);
}
void MainWindow::on_BoldAction_triggered()
{
    textBold();
}
void MainWindow::on_ItalicAction_triggered()
{
    textItalic();
}
void MainWindow::on_Underline_Action_triggered()
{
    textUnderline();
}
- QTextCharFormat是 Qt 框架中用于描述文本字符格式的类。它包含了关于文本外观的一些属性,比如字体、粗细、斜体等
- setFontWeight()-设置文本的字体粗细(权重)
- setFontItalic()-设置文本是否为斜体
- setFontUnderline() - 设置文本是否带下划线
字号字体
MainWindow.h添加声明
public:
``````
    void textFamily(const QString& fmly);
    void textSize(const QString &ps);    
private slots:
    void on_fontComboBox_activated(const QString &arg1);
    void on_textSizeComboBox_activated(const QString &arg1);
MainWindow.cpp添加定义
void MainWindow::textFamily(const QString &fmly)
{
    QTextCharFormat fmt;
    fmt.setFontFamily(fmly);
    if(activateChildWnd())
        activateChildWnd()->setFormatOnSelectedWord(fmt);
}
void MainWindow::textSize(const QString &ps)
{
    qreal pointSize = ps.toFloat();     //qreal就是double类型
    if(ps.toFloat() > 0)
    {
        QTextCharFormat fmt;
        fmt.setFontPointSize(pointSize);
        if(activateChildWnd())
            activateChildWnd()->setFormatOnSelectedWord(fmt);
    }
}
void MainWindow::on_fontComboBox_activated(const QString &arg1)
{
    textFamily(arg1);
}
void MainWindow::on_textSizeComboBox_activated(const QString &arg1)
{
    textSize(arg1);
}
- setFontFamily-设置文本的字体族(如 “Arial”, “Times New Roman” 等)
- setFontPointSize()- 设置文本的字体大小(以点为单位)
段落对齐
ChildWnd.h添加声明
`````
public:
	void setAlignOfDocumentText(int aligntype);
ChildWnd添加定义
``````
 void ChildWnd::setAlignOfDocumentText(int aligntype)
{
    if(aligntype == 1)
        setAlignment(Qt::AlignLeft | Qt::AlignAbsolute);
    else if(aligntype == 2)
        setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
    else if(aligntype == 3)
        setAlignment(Qt::AlignCenter | Qt::AlignAbsolute);
    else if(aligntype == 4)
        setAlignment(Qt::AlignJustify);
}
-  setAlignment是 QTextEdit或QTextDocument类的方法.用于设置当前光标所在段落的对齐方式。如果有选择区域,则对齐方式会应用到所有被选中的段落
-  Qt::AlignAbsolute的引入是为了确保对齐是基于绝对的左右方向,而不受布局方向的影响。当与Qt::AlignLeft或Qt::AlignRight一起使用时,它强制将对齐解释为绝对的左或右,而不考虑布局的书写方向。
MainWindow.cpp中initMainWindow方法最后添加如下代码:
``````
void MainWindow::initMainWindow()
{
        //保证互斥性,一次只能选择一个
    QActionGroup* alignGroup = new QActionGroup(this);
    alignGroup->addAction(ui->leftAlignAction);
    alignGroup->addAction(ui->RightAction);
    alignGroup->addAction(ui->CentreAction);
    alignGroup->addAction(ui->justifyAction);
}
- QActionGroup 类可以将多个 QAction组合在一起,形成一个动作组
MainWindow.h文件中添加声明:
void MainWindow::on_leftAlignAction_triggered()
{
    if(activateChildWnd())
        activateChildWnd()->setAlignOfDocumentText(1);
}
void MainWindow::on_RightAction_triggered()
{
    if(activateChildWnd())
        activateChildWnd()->setAlignOfDocumentText(2);
}
void MainWindow::on_CentreAction_triggered()
{
    if(activateChildWnd())
        activateChildWnd()->setAlignOfDocumentText(3);
}
void MainWindow::on_justifyAction_triggered()
{
    if(activateChildWnd())
        activateChildWnd()->setAlignOfDocumentText(4);
}
字体颜色
MainWindow.h添加声明
public:
void textColour();
private slots:
void on_ColorAction_triggered();
MainWindow.cpp添加定义
#include <QColorDialog>
void MainWindow::textColour()
{
    if(activateChildWnd())
    {
       QColor color = QColorDialog::getColor(activateChildWnd()->textColor(),this);
       if(!color.isValid()) return;
       QTextCharFormat fmt;
       fmt.setForeground(color);
       activateChildWnd()->setFormatOnSelectedWord(fmt);
       QPixmap pix(16,16);
       pix.fill(color);
       ui->ColorAction->setIcon(pix);
    }
}
void MainWindow::on_ColorAction_triggered()
{
    textColour();
}
- setForeground方法属于- QTextCharFormat类,用于设置文本的前景色,也就是文本的颜色。
项目符号
ChildWnd.h添加声明
public:
``````
    void setParaStyle(int pStyle);
ChildWnd.cpp添加定义
#include <QTextBlockFormat>
#include <QTextListFormat>
#include <QtWidgets>
``````
void ChildWnd::setParaStyle(int pStyle)
{
    QTextCursor tCursor = textCursor();
    QTextListFormat::Style sname;
    if(pStyle !=0)
    {
        switch(pStyle)
        {
        case 1:
            sname = QTextListFormat::ListDisc;  //黑色实心圆
            break;
        case 2:
            sname = QTextListFormat::ListCircle;    //空心圆
            break;
        case 3:
            sname = QTextListFormat::ListSquare;    //方形
            break;
        case 4:
            sname = QTextListFormat::ListDecimal;   //十进制整数
            break;
        case 5:
            sname = QTextListFormat::ListLowerAlpha;    //小写字母
            break;
        case 6:
            sname = QTextListFormat::ListUpperAlpha;    //大写字母
            break;
        case 7 :
            sname = QTextListFormat::ListLowerRoman;     //罗马小写
            break;
        case 8:
            sname  = QTextListFormat::ListUpperRoman;    //罗马大写
            break;
        default:
            sname = QTextListFormat::ListDisc;
        }
        tCursor.beginEditBlock();
        QTextBlockFormat tBlockFmt = tCursor.blockFormat();
        QTextListFormat tListFmt;
        if(tCursor.currentList())
        {
            tListFmt = tCursor.currentList()->format();
        }
        else
        {
            tListFmt.setIndent(tBlockFmt.indent() + 1);
            tBlockFmt.setIndent(0);
            tCursor.setBlockFormat(tBlockFmt);
        }
        tListFmt.setStyle(sname);
        tCursor.createList(tListFmt);
        tCursor.endEditBlock();
    }
    else
    {
        QTextBlockFormat tbfmt;
        tbfmt.setObjectIndex(-1);
        tCursor.mergeBlockFormat(tbfmt);
    }
}
-  beginEditBlock() - 这个方法允许在使用 QTextCursor对文档进行编辑-  例子: 
-  QTextEdit textEdit; QTextCursor cursor = textEdit.textCursor(); cursor.beginEditBlock(); // 开始编辑块 cursor.insertText("Hello, "); // 插入文本 cursor.insertText("world!"); // 插入更多文本 cursor.endEditBlock(); // 结束编辑块
 
-  
-  QTextBlockFormat类要用于设置和管理文本块(通常指段落)的格式 
-  QTextListFormat类用于设置和管理有序列表(编号)和无序列表(项目符号)的属性 
-  blockFormat()是QTextCursor类的一个方法,它用于获取光标当前位置的文本块(通常是一个段落)的格式
-  QTextCursor类的currentList()方法用来获取光标当前所在位置的列表对象,如果存在的话。这个方法返回一个指向QTextList的指针
-  format()方法是QTextList类的成员函数,用于获取关联列表的格式信息。这个方法返回一个QTextListFormat对象,它包含了定义列表样式和属性的各种设置,如列表的缩进、列表符号样式(例如圆点、数字等)、符号的对齐方式以及其他相关格式化选项。
-  setStyle()方法通常关联于QTextListFormat类,用于设置列表的样式。这个方法允许你指定列表的类型,例如是否为有序列表(数字、字母等)或无序列表(项目符号等)。
-  createList()方法用于创建列表,:该方法可以创建一个新的列表,并自动将当前光标位置的文本或新插入的文本块加入到这个列表中
-  tbfmt.setObjectIndex(-1)-setObjectIndex()方法通常在QTextBlockFormat中用来关联文本块格式与特定的对象。置objectIndex为-1可能是用来明确表示“没有任何关联对象”,或用于清除先前可能设置的任何对象关联
-  mergeBlockFormat()方法将tbfmt中的格式合并到光标当前位置的文本块中。由于在tbfmt中只修改了objectIndex(且设为-1),这通常意味着这是一个格式化的重置操作,用于移除可能的特定格式设置。
MainWindow.h添加成员方法、槽函数
public:
``````
    void paraStyle(int nStyle);
private slots:
``````
    void on_comboBox_activated(int index);
MainWindow.cpp添加定义
```````
void MainWindow::paraStyle(int nStyle)
{
    if(activateChildWnd())
        activateChildWnd()->setParaStyle(nStyle);
}
void MainWindow::on_comboBox_activated(int index)
{
    paraStyle(index);
}
打印预览
项目文件添加打印支持模块
QT       += core gui
QT       += printsupport
MainWindow.h头文件声明打印、预览的成员方法,槽方法:
#include <QtPrintSupport/QPrinter>
public:
``````
    void docPrint();
    void docPrintPreview();
private slots:
``````
    void printPreview(QPrinter* printer);
    void on_PrintPreAction_triggered();
MainWindow.cpp添加定义
``````
#include <QtPrintSupport/QPrinter>
#include <QtPrintSupport/QPrintDialog>
#include <QtPrintSupport/QPrintPreviewDialog>
void MainWindow::docPrint()
{
    QPrinter pter(QPrinter::HighResolution);
    QPrintDialog *ddlg = new QPrintDialog(&pter,this);
    if(activateChildWnd())
        ddlg->setOption(QAbstractPrintDialog::PrintSelection,true);
    ddlg->setWindowTitle("Print Document");
    ChildWnd* ChildWnd = activateChildWnd();
    if(ddlg->exec() == QDialog::Accepted)
        ChildWnd->print(&pter);
    delete ddlg;
}
void MainWindow::docPrintPreview()
{
    QPrinter pter;
    QPrintPreviewDialog preview(&pter,this);
    connect(&preview,SIGNAL(paintRequested(QPrinter*)),this,SLOT(printPreview(QPrinter*)));
    preview.exec();
}
void MainWindow::on_PrintAction_triggered()
{
    docPrint();
}
void MainWindow::printPreview(QPrinter *printer)
{
    activateChildWnd()->print(printer);
}
void MainWindow::on_PrintPreAction_triggered()
{
    docPrintPreview();
}
-  ddlg->setOption(QAbstractPrintDialog::PrintSelection,true);这行代码是用来配置打印对话框的行为的。具体来说,它设置了QPrintDialog对话框的一个选项,允许用户只打印选中的部分(而非整个文档)
-  exec()方法用来以模态方式运行对话框,并在对话框关闭时返回一个整数值。这个返回值通常是 ``QDialog::Accepted或QDialog::Rejected`
-  print()是QTextEdit类的一个方法,用于打印文档.
-  在 docPrintPreview()方法中, 需要connect(&preview,SIGNAL(paintRequested(QPrinter*)),this,SLOT(printPreview(QPrinter*)));因为如果不执行这个函数的话预览打印对话框会跳出,但是里面会没有内容. 当QPrintPreviewDialog需要渲染预览时,它会发出paintRequested信号。这个信号与MainWindow类的printPreview(QPrinter*)槽连接。这意味着一旦信号被触发,printPreview函数将被自动调用.而printPreview里面的print函数并不会执行实际的打印,而是渲染到预览对话框中.
-  所以, print函数不能直接被定义成物理打印,而是需要取决于QPrinter的对象配置,若QPrinter对象是带入到QPrintDiaglog的话就是物理打印,而带入QPrintPreviewDialog的话就是渲染预览打印.










