??在学习前面一些知识之后,开始进行第一个Qt实战任务,即完成一个多文档编辑器。首先先来看实验成果图,大概就是这么个多文档编辑器。 首先需要在设计模式里进行设计器的设置: 然后就是新建类MdiChild的声明和实现 mdichild.h
#ifndef MDICHILD_H
#define MDICHILD_H
#include<QTextEdit>
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;}
protected:
void closeEvent(QCloseEvent *event);
void contextMenuEvent(QContextMenuEvent *e);
private slots:
void documentWasModified();
private:
bool maybeSave();
void setCurrentFile(const QString &fileName);
QString curFile;
bool isUntitled;
};
#endif
mdichild.cpp
#include "mdichild.h"
#include<QFile>
#include<QMessageBox>
#include<QTextStream>
#include<QApplication>
#include<QFileInfo>
#include<QFileDialog>
#include<QCloseEvent>
#include<QPushButton>
#include<QMenu>
MdiChild::MdiChild(QWidget *parent):QTextEdit (parent)
{
setAttribute(Qt::WA_DeleteOnClose);
isUntitled = true;
}
void MdiChild::newFile()
{
static int sequenceNumber =1;
isUntitled =true;
curFile = tr("未命名文档%1.txt").arg(sequenceNumber++);
setWindowTitle(curFile +"[*]"+tr(" - 多文档编辑器"));
connect(document(),SIGNAL(contentsChanged()),this,SLOT(documentWasModified()));
}
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(),SIGNAL(contentsChanged()),this,SLOT(documentWasChanged()));
return true;
}
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;
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);
}
QString MdiChild::userFriendlyCurrentFile()
{
return QFileInfo(curFile).fileName();
}
void MdiChild::closeEvent(QCloseEvent *event)
{
if(maybeSave())
{
event->accept();
}
else {
event->ignore();
}
}
void MdiChild::contextMenuEvent(QContextMenuEvent *e)
{
QMenu *menu = new QMenu;
QAction *undo =menu->addAction(tr("撤销(&U)"),this,SLOT(undo()),QKeySequence::Undo);
undo->setEnabled(document()->isUndoAvailable());
QAction *redo =menu->addAction(tr("恢复(&R)"),this,SLOT(redo()),QKeySequence::Redo);
redo->setEnabled((document()->isRedoAvailable()));
menu->addSeparator();
QAction *cut =menu->addAction(tr("剪切(&T)"),this,SLOT(cut()),QKeySequence::Cut);
cut->setEnabled(textCursor().hasSelection());
QAction *copy = menu->addAction(tr("复制(&C)"),this,SLOT(copy()),QKeySequence::Copy);
copy->setEnabled(textCursor().hasSelection());
QAction *clear = menu->addAction(tr("清空"),this,SLOT(clear()));
clear->setEnabled(!document()->isEmpty());
menu->addSeparator();
QAction *select = menu->addAction(tr("全选"),this,SLOT(selectAll()),QKeySequence::SelectAll);
select->setEnabled(!document()->isEmpty());
menu->exec(e->globalPos());
delete menu;
}
void MdiChild::documentWasModified()
{
setWindowModified(document()->isModified());
}
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 *cancelBtn = box.addButton(tr("取消"),QMessageBox::RejectRole);
box.exec();
if(box.clickedButton() ==yesBtn)
return save();
else if(box.clickedButton() ==cancelBtn){
return false;
}
}
return true;
}
void MdiChild::setCurrentFile(const QString &fileName)
{
curFile = QFileInfo(fileName).canonicalFilePath();
isUntitled = false;
document()->setModified(false);
setWindowModified(false);
setWindowTitle(userFriendlyCurrentFile()+"[*]");
}
然后是mainwindow类的声明与实现 mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class QMdiSubWindow;
class MdiChild;
class QSignalMapper;
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void updateMenus();
MdiChild* createMdiChild();
void setActiveSubWindow(QWidget *window);
void updateWindowMenu();
void showTextRowAndCol();
void on_actionNew_triggered();
void on_actionOpen_triggered();
void on_actionSave_triggered();
void on_actionSaveAs_triggered();
void on_actionUndo_triggered();
void on_actionRedo_triggered();
void on_actionCut_triggered();
void on_actionCopy_triggered();
void on_actionPaste_triggered();
void on_actionClose_triggered();
void on_actionCloseAll_triggered();
void on_actionAbout_triggered();
void on_actionAboutQt_triggered();
void on_actionExit_triggered();
private:
QMdiSubWindow *findMdiChild(const QString &fileName);
void readSettings();
void writeSettings();
void initWindow();
protected:
void closeEvent(QCloseEvent* event);
private:
Ui::MainWindow *ui;
QAction *actionSeparator;
MdiChild *activeMdiChild();
QSignalMapper *windowMapper;
};
#endif
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include"mdichild.h"
#include<QFileDialog>
#include<QMdiSubWindow>
#include<QSignalMapper>
#include<QMessageBox>
#include<QSettings>
#include<QCloseEvent>
#include<QLabel>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
actionSeparator = new QAction(this);
actionSeparator->setSeparator(true);
updateMenus();
connect(ui->mdiArea,SIGNAL(subWindowActivated(QMdiSubWindow*)),this,SLOT(updateMenus()));
windowMapper = new QSignalMapper(this);
connect(windowMapper,SIGNAL(mapped(QWidget *)),this,SLOT(setActiveSubWindow(QWidget *)));
updateWindowMenu();
connect(ui->menu_W,SIGNAL(aboutToShow()),this,SLOT(updateWindowMenu()));
readSettings();
initWindow();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_actionNew_triggered()
{
MdiChild *child = createMdiChild();
child->newFile();
child->show();
}
void MainWindow::updateMenus()
{
bool hasMdiChild = (activeMdiChild()!= 0);
ui->actionSave->setEnabled(hasMdiChild);
ui->actionSaveAs->setEnabled(hasMdiChild);
ui->actionPaste->setEnabled(hasMdiChild);
ui->actionClose->setEnabled(hasMdiChild);
ui->actionCloseAll->setEnabled(hasMdiChild);
ui->actionTile->setEnabled(hasMdiChild);
ui->actionCascade->setEnabled(hasMdiChild);
ui->actionNext->setEnabled(hasMdiChild);
ui->actionPrevious->setEnabled(hasMdiChild);
bool hasSelection =(activeMdiChild() &&activeMdiChild()->textCursor().hasSelection());
ui->actionCut->setEnabled(hasSelection);
ui->actionPaste->setEnabled(hasSelection);
ui->actionUndo->setEnabled(activeMdiChild()&&activeMdiChild()->document()->isUndoAvailable());
ui->actionRedo->setEnabled(activeMdiChild()&&activeMdiChild()->document()->isRedoAvailable());
}
MdiChild *MainWindow::createMdiChild()
{
MdiChild *child = new MdiChild;
ui->mdiArea->addSubWindow(child);
connect(child,SIGNAL(copyAvailable(bool)),ui->actionCut,SLOT(setEnabled(bool)));
connect(child,SIGNAL(copyAvailable(bool)),ui->actionCopy,SLOT(setEnabled(bool)));
connect(child->document(),SIGNAL(undoAvailable(bool)),ui->actionUndo,SLOT(setEnabled(bool)));
connect(child->document(),SIGNAL(redoAvailable(bool)),ui->actionRedo,SLOT(setEnabled(bool)));
connect(child,SIGNAL(cursorPositionChanged()),this,SLOT(showTextRowAndCol()));
return child;
}
void MainWindow::setActiveSubWindow(QWidget *window)
{
if(!window)
return;
ui->mdiArea->setActiveSubWindow(qobject_cast<QMdiSubWindow*>(window));
}
void MainWindow::on_actionOpen_triggered()
{
QString fileName = QFileDialog::getOpenFileName(this);
if(!fileName.isEmpty())
{
QMdiSubWindow *existing = findMdiChild(fileName);
if(existing)
{
ui->mdiArea->setActiveSubWindow(existing);
return;
}
MdiChild *child = createMdiChild();
if(child->loadFile(fileName))
{
ui->statusBar->showMessage(tr("打开文件成功"),2000);
child->show();
}
else {
child->close();
}
}
}
void MainWindow::updateWindowMenu()
{
ui->menu_W->clear();
ui->menu_W->addAction(ui->actionClose);
ui->menu_W->addAction(ui->actionCloseAll);
ui->menu_W->addSeparator();
ui->menu_W->addAction(ui->actionTile);
ui->menu_W->addAction(ui->actionCascade);
ui->menu_W->addSeparator();
ui->menu_W->addAction(ui->actionNext);
ui->menu_W->addAction(ui->actionPrevious);
ui->menu_W->addAction(actionSeparator);
QList<QMdiSubWindow*> windows =ui->mdiArea->subWindowList();
actionSeparator->setVisible(!windows.isEmpty());
for(int i=0;i<windows.size();++i)
{
MdiChild *child = qobject_cast<MdiChild*>(windows.at(i)->widget());
QString text;
if(i<9)
{
text=tr("&%1 %2").arg(i+1).arg(child->userFriendlyCurrentFile());
}
else {
text=tr("%1 %2").arg(i+1).arg(child->userFriendlyCurrentFile());
}
QAction *action = ui->menu_W->addAction(text);
action->setCheckable(true);
action->setChecked(child ==activeMdiChild());
connect(action,SIGNAL(triggered()),windowMapper,SLOT(map()));
windowMapper->setMapping(action,windows.at(i));
}
}
void MainWindow::showTextRowAndCol()
{
if(activeMdiChild())
{
int rowNum = activeMdiChild()->textCursor().blockNumber()+1;
int colNum = activeMdiChild()->textCursor().blockNumber()+1;
ui->statusBar->showMessage(tr("%1行 %2列").arg(rowNum).arg(colNum),2000);
}
}
QMdiSubWindow *MainWindow::findMdiChild(const QString &fileName)
{
QString canonicalFilePath = QFileInfo(fileName).canonicalFilePath();
foreach(QMdiSubWindow* window,ui->mdiArea->subWindowList())
{
MdiChild* mdiChild = qobject_cast<MdiChild*>(window->widget());
if(mdiChild->currentFile() ==canonicalFilePath)
return window;
}
return 0;
}
void MainWindow::readSettings()
{
QSettings settings("yafeilinux","myMdi");
QPoint pos =settings.value("pos",QPoint(200,200)).toPoint();
QSize size =settings.value("size",QSize(400,200)).toSize();
move(pos);
resize(size);
}
void MainWindow::writeSettings()
{
QSettings settings("yafeilinux","myMdi");
settings.setValue("pos",pos());
settings.setValue("size",size());
}
void MainWindow::initWindow()
{
setWindowTitle(tr("多文档编辑器"));
ui->mainToolBar->setWindowTitle(tr("工具栏"));
ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
ui->statusBar->showMessage(tr("欢迎使用多文档编辑器"));
QLabel *label =new QLabel(this);
label->setFrameStyle(QFrame::Box|QFrame::Sunken);
label->setText(tr("<a href=\"http://www.yafeilinux.com\">yafeilinux.com</a>"));
label->setTextFormat(Qt::RichText);
label->setOpenExternalLinks(true);
ui->statusBar->addPermanentWidget(label);
ui->actionNew->setStatusTip(tr("创建一个文件"));
ui->actionOpen->setStatusTip(tr("打开一个文件"));
}
void MainWindow::closeEvent(QCloseEvent *event)
{
ui->mdiArea->closeAllSubWindows();
if(ui->mdiArea->currentSubWindow())
event->ignore();
else {
writeSettings();
event->accept();
}
}
MdiChild *MainWindow::activeMdiChild()
{
if(QMdiSubWindow *activeSubWindow = ui->mdiArea->activeSubWindow())
return qobject_cast<MdiChild*>(activeSubWindow->widget());
return 0;
}
void MainWindow::on_actionSave_triggered()
{
if(activeMdiChild()&&activeMdiChild()->save())
ui->statusBar->showMessage(tr("文件保存成功"),2000);
}
void MainWindow::on_actionSaveAs_triggered()
{
if(activeMdiChild()&&activeMdiChild()->saveAs())
ui->statusBar->showMessage(tr("文件保存成功"),2000);
}
void MainWindow::on_actionUndo_triggered()
{
if(activeMdiChild())
activeMdiChild()->undo();
}
void MainWindow::on_actionRedo_triggered()
{
if(activeMdiChild())
activeMdiChild()->redo();
}
void MainWindow::on_actionCut_triggered()
{
if(activeMdiChild())
activeMdiChild()->cut();
}
void MainWindow::on_actionCopy_triggered()
{
if(activeMdiChild())
activeMdiChild()->copy();
}
void MainWindow::on_actionPaste_triggered()
{
if(activeMdiChild())
activeMdiChild()->paste();
}
void MainWindow::on_actionClose_triggered()
{
ui->mdiArea->closeActiveSubWindow();
}
void MainWindow::on_actionCloseAll_triggered()
{
ui->mdiArea->closeAllSubWindows();
}
void MainWindow::on_actionAbout_triggered()
{
QMessageBox::about(this,"关于",tr("致力于多文档编辑器普及工作"));
}
void MainWindow::on_actionAboutQt_triggered()
{
QMessageBox::about(this,"关于Qt",tr("Qt是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。"));
}
void MainWindow::on_actionExit_triggered()
{
qApp->closeAllWindows();
}
最后是main函数
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
|