为了更好的在MDI Area中添加子窗口,那就子类化子窗口,它继承自 QTexiEdit 类。
选择新建一个 类
类名为: MdiChild,继承自 QTextEdit。
#ifndef MDICHILD_H
#define MDICHILD_H
#include // 头文件包含
#include // 头文件包含
class MdiChild : public QTextEdit
{
Q_OBJECT
public:
explicit MdiChild(QWidget* parent = 0);
void newFile(); // 新建操作
bool loadFile(const QString &fileName); // 加载文件
bool save(); // 保存操作
bool saveAs(); // 另存为操作
bool saveFile(const QString &fileName); // 保存文件
QString userFriendlyCurrentFile(); // 提取文件名(只包含文件名,用户友好的~)
QString currentFile(){return curFile;} // 返回当前文件路径(返回文件的pathname)
protected:
void closeEvent(QCloseEvent* event); // 关闭事件
private:
void documentWasModified(); // 文件被更改时,显示更改状态标志
private:
bool maybeSave(); // 判断是否需要保存
void setCurrentFile(const QString &filename); // 设置当前文件
QString curFile; // 保存当前文件路径 pathname
bool isUntitled; // 作为是否保存到硬盘上的标志
};
#endif // MDICHILD_H
对于子窗口,需要有一些基本功能,这些功能由类中的函数来实现。
主要包括的功能有:
(1)新建文件,对应成员函数 newFile
这里的新建文件不是主窗口中点击"new"对应的槽函数,而是在已经有了子窗口之后的新建文件。
点击"New"会创建新的子窗口,创建新的子窗口之后,子窗口会执行"新建文件",主要作用是子窗口的标题栏的文件名,还有就是文件被改动之后显示 " [*] "。
void MdiChild::newFile()
{
static int sequenceNumber = 1;
isUntitled = true;
curFile = tr("新建文档 %1.txt").arg(sequenceNumber++);
setWindowTitle(curFile + "[*]" + tr("-多文档编辑器"));
connect(document(), &QTextDocument::contentsChanged, this, &MdiChild::documentWasModified);
}
**(2)加载文件,对应的成员函数 loadFile **
功能就是:把文件的内容读到子窗口中。具体就是给子窗口一个文件名,子窗口就根据文件名把文件的内容全部读到子窗口中。
bool MdiChild::loadFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QFile::ReadOnly | QFile::Text)){
QMessageBox::warning(this, tr("多文档编辑器"), tr("无法读取文件 %1:\n%2.").arg(fileName).arg(file.errorString()));
return false;
}
QTextStream in(&file); // 新建文本流对象
QApplication::setOverrideCursor(Qt::WaitCursor); // 设置鼠标状态为等待状态
setPlainText(in.readAll()); // 读取文件的全部内容
QApplication::restoreOverrideCursor(); // 恢复鼠标状态
setCurrentFile(fileName); // 设置当前文件
connect(document(), &QTextDocument::contentsChanged, this, &MdiChild::documentWasModified);
return true;
}
(3)保存文件操作
保存其实分两种,一种是另存为(不知道要存的的路径,需要先询问),另一种是保存(已经知道路径,直接保存到已知路径中)。
有一个变量 isUntitled 来作为是否保存到硬盘上的标志。例如,在新建窗口时,isUntitled 就为true,表示没有保存到硬盘上,而打开文件(也就是加载文件)时,isUntitled为false。因为是从硬盘加载来的。
所以关于保存操作,要先判断 isUntitled, 然后执行saveFile 或 saveAs。
bool MdiChild::save()
{
if (isUntitled){
return saveAs();
}
else{
return saveFile(curFile);
}
}
bool MdiChild::saveAs()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("另存为"), curFile);
if (fileName.isEmpty()){
return false;
}
else{
return saveFile(fileName);
}
}
bool MdiChild::saveFile(const QString &fileName)
{
QFile file(fileName);
if (!file.open(QFile::WriteOnly | QFile::Text)){
QMessageBox::warning(this, tr("多文档编辑器"), tr("无法写入文件%1:\n%2.").arg(fileName).arg(file.errorString()));
return false;
}
QTextStream out(&file);
QApplication::setOverrideCursor(Qt::WaitCursor);
out << toPlainText();
QApplication::restoreOverrideCursor();
setCurrentFile(fileName);
return true;
}
saveAs——另存为,就是先获取要保存的路径,然后执行保存文件。
保存文件就是把子窗口中的内容写到硬盘中的一个文件中去。
(4)获取文件名
curFile是私有成员变量,表示子窗口的文件的路径名(pathname),userFriendlyCurrentFile是用户友好的意思,是只获取它的文件名name。去掉path。
QString MdiChild::userFriendlyCurrentFile()
{
return QFileInfo(curFile).fileName();
}
(5)获取pathname
直接返回私有成员变量curFile的值。
QString currentFile(){return curFile;}
(6)关闭事件
用户点击了关闭,要进行一些判断啊,(用户修改了文件并没有保存的情况)
void MdiChild::closeEvent(QCloseEvent *event)
{
if (maybeSave())
event->accept();
else
event->ignore();
}
只有maybeSave() 为 1 时,才可以关闭子窗口。接下来看 maybeSave() 是什么样的。
(7)maybesave 关闭之前的判断
bool MdiChild::maybeSave()
{
if (document()->isModified()){
QMessageBox box;
box.setWindowTitle(tr("多文档编辑器"));
box.setText(tr("是否保存对“%1”的修改?").arg(userFriendlyCurrentFile()));
box.setIcon(QMessageBox::Warning);
QPushButton* yesBtn = box.addButton(tr("是(&Y)"), QMessageBox::YesRole);
box.addButton(tr("否(&N)"), QMessageBox::NoRole);
QPushButton* cancleBtn = box.addButton(tr("取消"), QMessageBox::RejectRole);
box.exec();
if (box.clickedButton() == yesBtn)
return save();
else if (box.clickedButton() == cancleBtn)
return false;
}
return true;
}
从函数可以看出,当用户修改了内容且没保存时,回弹出窗口询问是否保存,当正确保存(点击是) 或者 决定不保存(点击否),都会返回 true。当用户点击取消时,返回false。
结合关闭事件来看,当返回true时会关闭。反之则不关闭子窗口。
(8)设置当前文件
作用有三:
一、更新成员变量curFile.
二、更新isUntitled,作为已经保存在硬盘上的标志。
三、子窗口标题不显示 [*]。
void MdiChild::setCurrentFile(const QString &filename)
{
curFile = QFileInfo(filename).canonicalFilePath();
isUntitled = false;
document()->setModified(false);
setWindowTitle(userFriendlyCurrentFile() + "[*]");
}
(9)最后一个函数 文件内容被更改后
void MdiChild::documentWasModified()
{
setWindowModified(document()->isModified());
}
这个函数的目的是,显示 “[*]”,这是调用Qt 自带的函数。
至此,子窗口的类写完了,接下来就是把主窗口中的动作(action)和子窗口中的函数相关联起来。