5.1 Qt5主窗体的基本构成
5.1.1 Qt5主窗体的基本构成
Qt5中有一个专门的窗体类来描述主窗体,就是QMainWindow,这个类与QDialog,QWidget这些与Widget部件有关的类构成了Qt的基本三大Widget部件。而Qt5的主窗体中又包含了标题栏,菜单栏,工具栏,锚接部件,中心部件以及状态栏构成。如下图所示:

菜单栏:菜单栏包含了多个菜单,而每一个菜单则由多个菜单项构成,实际上这些菜单项都是由一些QAction类型的动作构成。一个主窗体只能有一个菜单栏。
工具栏:工具栏是用来盛放各种工具部件的,它也是由一系列的QAction构成的动作集,而与菜单栏不同的是,同一个主窗口中可以有多个工具栏。
状态栏:状态栏是用于显示当用户执行了某种操作后,需要在整个主窗口的底部进行显示该操作的执行状态或执行结果。它位于主窗口的最底部。
锚接部件:锚接部件充当了一个容器的角色,该区域可以用来放置Qt中其他的窗体类型的Widget或Widget容器部件。比如DockWidget,StackWidget等。它位于中心部件区的上方。
中心部件:中心部件位于主窗口的中心,一个主窗口只能有一个中心部件。
注:从上述结构图来看,主窗体似乎在某种意义上具有了布局,因此QMainWindow具有自己独立的布局管理器,所以说不能在QMainWindow上直接创建布局管理器,但是,只能在QMainWindow的中心部件上创建或设置布局管理器则是允许的。
5.1.2 典型案例(文本编辑器)
为了彻底理解上述关于QMainWindow的一些细节,这里我们尝试做一个简单的文本编辑器来说明QMainWindow的一些使用方法。
该文本编辑器具有文件的操作功能,包括新建一个文本文件,使用QFileDialog来打开一个已经存在的文件,使用QFile和QTextStream来打开并读取文件的内容,打印文件(包括文本打印和图像打印)这会涉及到QPrinterDialog、QPrinter的基本使用。
另外,该文本编辑器在对图片进行处理时,还需要图片的缩放,旋转及镜像等坐标的变换,这里面会使用到QMatrix对图片进行这些操作。
开发文本的编辑功能,通过在工具栏上添加设置文字字体的Comobox,字号大小的SpinBox、加粗、斜体、下划线和字体颜色的按钮和控件的实现。
最后是排版功能,通过选择某种排序方式来实现文本的排序,以及实现文本对齐(左对齐、右对齐、居中对齐和两端对齐)以及撤销和重做的方法。
具体的代码步骤如下:
(1)新建Qt GUI应用,项目名称为ImageProcessor,基类选择QMainWindow,类名为ImgProcessor,不需要创建ui界面。
(2)在项目中新建一个C++的类,类名为ShowWidget,该类的基类则为QWidget。
(3)在项目中新建一个C++类,类名为ShowWidget,在showwidget.h中写上以下内容:
#ifndef SHOWWIDGET_H
#define SHOWWIDGET_H
#include <QWidget>
#include <QImage>
#include <QTextEdit>
#include <QLabel>
#include <QHBoxLayout>
#include <QSplitter>
class ShowWidget : public QWidget
{
Q_OBJECT
public:
explicit ShowWidget(QWidget *parent = nullptr);
public:
QImage m_img;//图片
QLabel*m_imageLabel;//用于显示图片的Label
QTextEdit*m_text;//文本编辑的Widget
signals:
public slots:
private:
QHBoxLayout*m_mainLayout;//主布局
};
#endif // SHOWWIDGET_H
(4)在showwidget.cpp中写以下内容
#include "showwidget.h"
ShowWidget::ShowWidget(QWidget *parent):QWidget(parent),m_mainLayout(new QHBoxLayout(this))
{
m_imageLabel = new QLabel;
m_imageLabel->setScaledContents(true);
m_text = new QTextEdit;
m_mainLayout->addWidget(m_imageLabel);
m_mainLayout->addWidget(m_text);
}
(5)imgprocessor.h中定义以下成员和函数
#ifndef IMGPROCESSOR_H
#define IMGPROCESSOR_H
#include <QMainWindow>
#include <QImage>
#include <QLabel>
#include <QMenu>
#include <QMenuBar>
#include <QPainter>
#include <QToolBar>
#include <QMessageBox>
#include <QAction>
#include <QFileDialog>
#include <QTextStream>
#include <QPrintDialog>
#include <QPrinter>
#include <QMatrix>
#include <QIODevice>
#include <QComboBox>
#include <QSpinBox>
#include <QFontComboBox>
#include <QToolButton>
#include <QTextCharFormat>
#include <QApplication>
#include "showwidget.h"
class ImgProcessor : public QMainWindow
{
Q_OBJECT
public:
ImgProcessor(QWidget *parent = nullptr);
~ImgProcessor();
public:
void createActions(); //创建所有的动作(包括菜单的菜单项动作和工具栏的动作)
void createMenus(); //创建菜单
void createToolBars(); //创建工具栏
void loadFile(QString fileName);// 加载文件
void mergeFormat(QTextCharFormat);// 合并文本格式
private:
QMenu*m_fileMenu;
QMenu*m_zoomMenu;
QMenu*m_rotatemenu;
QMenu*m_mirrorMenu;
QImage m_img;
QString m_fileName;
ShowWidget*m_showWidget;
//文件菜单项的相关动作
QAction*m_openFileAction;
QAction*m_newFileAction;
QAction*m_printTextAction;
QAction*m_printImageAction;
QAction*m_exitAction;
//编辑菜单项
QAction*m_copyAction;
QAction*m_cutAction;
QAction*m_pasteAction;
QAction*m_aboutAction;
QAction*m_zoomInAction;
QAction*m_zoomOutAction;
//旋转菜单项
QAction*m_rotate90Action;
QAction*m_rotate180Action;
QAction*m_rotate270Action;
//镜像菜单项
QAction*m_mirrorVerticalAction;
QAction*m_mirrorHorizontalAction;
QAction*m_undoAction;
QAction*m_redoAction;
QToolBar*m_fileToolBar;
QToolBar*m_zoomToolBar;
QToolBar*m_rotateToolBar;
QToolBar*m_mirrorToolBar;
QToolBar*m_doToolBar;
protected slots:
void ShowNewFile();//处理新建文件的动作
void ShowOpenFile();//处理打开文件的动作
void ShowPrintText();//打印文本文件
void ShowPrintImage();//打印图片
void ShowZoomIn();//放大图片
void ShowZoomOut();//缩小图片
void ShowRotate90();//旋转90度
void ShowRotate180();//旋转180度
void ShowRotate270();//旋转270度
void ShowMirrorHorizontal();//水平镜像
void ShowMirrorVertical();//垂直镜像
};
#endif // IMGPROCESSOR_H
(6)在imgprocessor.cpp实现以下内容
#include "imgprocessor.h"
ImgProcessor::ImgProcessor(QWidget *parent)
: QMainWindow(parent)
{
this->setWindowTitle(tr("Easy Worder"));//设置主窗体标题
m_showWidget = new ShowWidget(this);//new一个ShowWidget
this->setCentralWidget(m_showWidget);//将其设置为中心窗体
createActions();
createMenus();
createToolBars();
m_img.load(":/image.png");
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
ImgProcessor::~ImgProcessor()
{
}
void ImgProcessor::createActions()
{
/// 文件菜单项的相关动作
//打开
m_openFileAction = new QAction(QIcon(":/open.png"),"打开",this);
m_openFileAction->setShortcut(tr("Ctrl+O"));//设置动作被触发时的快捷键
m_openFileAction->setStatusTip(tr("打开一个文件"));//设置状态条显示,当鼠标光标停靠在该菜单项时,会出现提示
connect(m_openFileAction,&QAction::triggered,this,&ImgProcessor::ShowOpenFile);
//新建
m_newFileAction = new QAction(QIcon(":/new.png"),"新建",this);
m_newFileAction->setShortcut(tr("Ctrl+N"));
m_newFileAction->setStatusTip(tr("新建一个文件"));
connect(m_newFileAction,&QAction::triggered,this,&ImgProcessor::ShowNewFile);
//退出
m_exitAction = new QAction("退出",this);
m_exitAction->setShortcut(tr("Ctrl+Q"));
m_exitAction->setStatusTip(tr("退出程序"));
connect(m_exitAction,&QAction::triggered,this,&QMainWindow::close);
//复制
m_copyAction = new QAction(QIcon(":/copy.png"),tr("复制"),this);
m_copyAction->setShortcut(tr("Ctrl+C"));
m_copyAction->setStatusTip(tr("复制文件"));
connect(m_copyAction,SIGNAL(triggered()),this->m_showWidget->m_text,SLOT(copy()));
//剪切
m_cutAction = new QAction(QIcon(":/cut.png"),tr("剪切"),this);
m_cutAction->setShortcut(tr("Ctrl+X"));
m_cutAction->setStatusTip(tr("剪切文件"));
//粘贴
m_pasteAction = new QAction(QIcon(":/paste.png"),tr("粘贴"),this);
m_pasteAction->setShortcut(tr("Ctrl+V"));
m_pasteAction->setStatusTip(tr("粘贴文件"));
connect(m_pasteAction,SIGNAL(triggered()),this->m_showWidget->m_text,SLOT(paste()));
//关于
m_aboutAction = new QAction(tr("关于"),this);
connect(m_aboutAction,&QAction::triggered,this,&QApplication::aboutQt);
//打印文本
m_printTextAction = new QAction(QIcon(":/printtext.png"),tr("打印文本"),this);
m_printTextAction->setStatusTip(tr("打印一个文本"));
connect(m_printTextAction,&QAction::triggered,this,&ImgProcessor::ShowPrintText);
//打印图像
m_printImageAction = new QAction(QIcon(":/printpicture.png"),tr("打印图像"),this);
m_printImageAction->setStatusTip(tr("打印一幅图像"));
connect(m_printImageAction,&QAction::triggered,this,&ImgProcessor::ShowPrintImage);
//放大
m_zoomInAction = new QAction(QIcon(":/max.png"),tr("放大"),this);
m_zoomInAction->setStatusTip(tr("放大图片"));
connect(m_zoomInAction,&QAction::triggered,this,&ImgProcessor::ShowZoomIn);
//缩小
m_zoomOutAction = new QAction(QIcon(":/min.png"),tr("缩小"),this);
m_zoomOutAction->setStatusTip(tr("缩小图片"));
connect(m_zoomOutAction,&QAction::triggered,this,&ImgProcessor::ShowZoomOut);
//旋转90
m_rotate90Action = new QAction(QIcon(":/rotate90.png"),tr("旋转90°"),this);
m_rotate90Action->setStatusTip(tr("将图片旋转90°"));
connect(m_rotate90Action,&QAction::triggered,this,&ImgProcessor::ShowRotate90);
//旋转180
m_rotate180Action = new QAction(QIcon(":/rotate180.png"),tr("旋转180°"),this);
m_rotate180Action->setStatusTip(tr("将图片旋转180°"));
connect(m_rotate180Action,&QAction::triggered,this,&ImgProcessor::ShowRotate180);
//旋转270
m_rotate270Action = new QAction(QIcon(":/rotate270.png"),tr("旋转270°"),this);
m_rotate270Action->setStatusTip(tr("将图片旋转270°"));
connect(m_rotate270Action,&QAction::triggered,this,&ImgProcessor::ShowRotate270);
//垂直镜像
m_mirrorVerticalAction = new QAction(QIcon(":/mirrorvertical.png"),tr("垂直镜像"),this);
m_mirrorVerticalAction->setStatusTip(tr("对图片进行垂直镜像"));
connect(m_mirrorVerticalAction,&QAction::triggered,this,&ImgProcessor::ShowMirrorVertical);
//水平镜像
m_mirrorHorizontalAction = new QAction(QIcon(":/mirrorhorizontal.png"),tr("水平镜像"),this);
m_mirrorHorizontalAction->setStatusTip(tr("对图片进行水平镜像"));
connect(m_mirrorHorizontalAction,&QAction::triggered,this,&ImgProcessor::ShowMirrorHorizontal);
//撤销
m_undoAction = new QAction(QIcon(":/undo.png"),tr("撤销"),this);
m_undoAction->setShortcut(tr("Ctrl+Z"));
m_undoAction->setStatusTip(tr("撤销操作"));
connect(m_undoAction,SIGNAL(triggered()),m_showWidget->m_text,SLOT(undo()));
//恢复
m_redoAction = new QAction(QIcon(":/redo.png"),tr("重做"),this);
m_redoAction->setShortcut(tr("Ctrl+R"));
m_redoAction->setStatusTip(tr("重做"));
connect(m_redoAction,SIGNAL(triggered()),m_showWidget->m_text,SLOT(redo()));
}
void ImgProcessor::createMenus()
{
///文件菜单
m_fileMenu = new QMenu("文件",this);
m_fileMenu->addAction(m_openFileAction);
m_fileMenu->addAction(m_newFileAction);
m_fileMenu->addAction(m_printTextAction);
m_fileMenu->addAction(m_printImageAction);
m_fileMenu->addSeparator();//菜单项和菜单项之间添加一个分割线,更美观
m_fileMenu->addAction(m_exitAction);
this->menuBar()->addMenu(m_fileMenu);
///编辑菜单
m_zoomMenu = menuBar()->addMenu("编辑");//也可以通过这种方式来创建出菜单并往这个菜单的指针里面添加菜单项
m_zoomMenu->addAction(m_copyAction);
m_zoomMenu->addAction(m_cutAction);
m_zoomMenu->addAction(m_pasteAction);
m_zoomMenu->addAction(m_aboutAction);
m_zoomMenu->addSeparator();//菜单项和菜单项之间添加一个分割线,更美观
m_zoomMenu->addAction(m_zoomInAction);
m_zoomMenu->addAction(m_zoomOutAction);
///旋转菜单
m_rotatemenu = new QMenu(tr("旋转"),this);
m_rotatemenu->addAction(m_rotate90Action);
m_rotatemenu->addAction(m_rotate180Action);
m_rotatemenu->addAction(m_rotate270Action);
this->menuBar()->addMenu(m_rotatemenu);
///镜像菜单
m_mirrorMenu = new QMenu(tr("镜像"),this);
m_mirrorMenu->addAction(m_mirrorVerticalAction);
m_mirrorMenu->addAction(m_mirrorHorizontalAction);
this->menuBar()->addMenu(m_mirrorMenu);
}
void ImgProcessor::createToolBars()
{
//文件工具栏
m_fileToolBar = this->addToolBar(tr("文件"));//通过这种方式添加工具栏(和前面的添加菜单的方式一样的)
m_fileToolBar->addAction(m_openFileAction);
m_fileToolBar->addAction(m_newFileAction);
m_fileToolBar->addAction(m_printTextAction);
m_fileToolBar->addAction(m_printImageAction);
//编辑工具栏
m_zoomToolBar = new QToolBar(tr("编辑"),this);
m_zoomToolBar->addAction(m_copyAction);
m_zoomToolBar->addAction(m_cutAction);
m_zoomToolBar->addAction(m_pasteAction);
m_zoomToolBar->addSeparator();//工具项和工具项之间添加一个分割符,更美观
m_zoomToolBar->addAction(m_zoomInAction);
m_zoomToolBar->addAction(m_zoomOutAction);
this->addToolBar(m_zoomToolBar);//也可以通过这种方式添加工具栏
/*
*其中,addToolBar可以指定所添加的ToolBar可以在窗口中所停靠的区域,有以下的枚举来描述:
* LeftToolBarArea (只能在左边停靠)
* RightToolBarArea (只能在右边停靠)
* TopToolBarArea (只能停靠在顶部)
* BottomToolBarArea (只能停靠在底部)
* AllToolBarAreas (所有的地方都可以停靠)==>默认
* NoToolBarArea (不允许停靠工具栏)
* 同样的,也可以使用QToolBar::setAllowedAreas()来设置工具栏可停靠的位置
* QToolBar::setMovable(bool)可设置工具栏的可移动性
*/
///旋转工具栏
m_rotateToolBar = addToolBar("旋转");
m_rotateToolBar->addAction(m_rotate90Action);
m_rotateToolBar->addAction(m_rotate180Action);
m_rotateToolBar->addAction(m_rotate270Action);
///撤销和重做工具栏
m_doToolBar = new QToolBar("DoEdit",this);
m_doToolBar->addAction(m_undoAction);
m_doToolBar->addAction(m_redoAction);
this->addToolBar(m_doToolBar);
}
//读文件到文件编辑器中
void ImgProcessor::loadFile(QString fileName)
{
QFile file(fileName);
if(file.open(QIODevice::ReadOnly|QIODevice::Text))//判断文件是否被打开且是否为纯文本文件
{
QTextStream textStream(&file);
do
{
m_showWidget->m_text->append(textStream.readLine());
}while(!file.atEnd());
}
}
void ImgProcessor::mergeFormat(QTextCharFormat)
{
}
//点击"文件->新建"后会重新再创建一个Easy Worder的窗口
void ImgProcessor::ShowNewFile()
{
ImgProcessor*imgProcessor = new ImgProcessor;
imgProcessor->show();
}
//打开文件后,读入文件的内容
void ImgProcessor::ShowOpenFile()
{
m_fileName = QFileDialog::getOpenFileName(this);
if(!m_fileName.isEmpty())
{
if(m_showWidget->m_text->document()->isEmpty())
{
loadFile(m_fileName);
}
else
{
ImgProcessor*imgProcessor = new ImgProcessor;
imgProcessor->show();
imgProcessor->loadFile(m_fileName);
}
}
}
//处理打印文本的槽函数
void ImgProcessor::ShowPrintText()
{
//对于文本文件的打印,则需要用到QTextDocument的print方法,但该方法需要一个QPrinter对象,并且需要配合标准打印对话框来完成
QPrinter printer;
QPrintDialog printDialog(&printer,this);
if(QPrintDialog::Accepted == printDialog.exec())
{
QTextDocument*doc = m_showWidget->m_text->document();
doc->print(&printer);
}
}
//打印图片
void ImgProcessor::ShowPrintImage()
{
//图片的打印也是一样,因为打印机可以被抽象为也是一种IO设备,那么我相当于就是直接通过QPainter往我的打印机上绘图,这个过程抽象处理就是打印图片的过程
QPrinter printer;
QPrintDialog printDialog(&printer,this);
if(QPrintDialog::Accepted == printDialog.exec())
{
QPainter painter(&printer);//构造一个Painter,并将打印机作为绘制图片的对象
QRect rect = painter.viewport();//获取QPainter对象的视口矩形区域
QSize size = m_img.size();//获取图像的大小
size.scale(rect.size(),Qt::KeepAspectRatio);//按照图像的比例大小重新设置视口的矩形区域
painter.setViewport(rect.x(),rect.y(),size.width(),size.height());//设置视口的矩形区域
painter.setWindow(m_img.rect());//设置QPainter窗口大小为图像的大小
painter.drawImage(0,0,m_img);//将图片绘制到打印机上
}
}
void ImgProcessor::ShowZoomIn()
{
QMatrix matrix;//构造一个QMatrix实例
if(m_img.isNull())//判断图像是否有效
{
return ;
}
else
{
matrix.scale(2,2);//按照2倍的比例对图片进行放大(水平方向2倍,垂直方向2倍)
m_img = m_img.transformed(matrix);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
void ImgProcessor::ShowZoomOut()
{
QMatrix matrix;
if(m_img.isNull())
{
return;
}
else
{
matrix.scale(0.5,0.5);
m_img = m_img.transformed(matrix);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
//旋转90度
void ImgProcessor::ShowRotate90()
{
if(m_img.isNull())
{
return;
}
else
{
QMatrix matrix;
matrix.rotate(90);
m_img = m_img.transformed(matrix);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
//旋转180度
void ImgProcessor::ShowRotate180()
{
if(m_img.isNull())
{
return;
}
else
{
QMatrix matrix;
matrix.rotate(180);
m_img = m_img.transformed(matrix);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
//旋转270度
void ImgProcessor::ShowRotate270()
{
if(m_img.isNull())
{
return;
}
else
{
QMatrix matrix;
matrix.rotate(270);
m_img = m_img.transformed(matrix);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
//水平镜像
void ImgProcessor::ShowMirrorHorizontal()
{
if(m_img.isNull())
{
return;
}
else
{
m_img = m_img.mirrored(true,false);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
//垂直镜像
void ImgProcessor::ShowMirrorVertical()
{
if(m_img.isNull())
{
return;
}
else
{
m_img = m_img.mirrored(false,true);
m_showWidget->m_imageLabel->setPixmap(QPixmap::fromImage(m_img));
}
}
(7)最终效果图如图所示

5.1.3 小结
1.在Qt中,主窗口使用QMainWindow来进行描述。它至少包含了标题栏、菜单栏、工具栏、锚接部件、中心部件以及状态栏这几部分构成。
2.对于标题栏设置文本,可以使用setWindowTitle函数来进行设置。而向主窗口中的中菜单栏添加菜单项则使用到了QMainWindow::menuBar()->addMenu()方法,我们可以直接向其添加菜单,并通过其返回的Menu指针,向该指针对应的Menu中添加菜单项。也可以自己先定义好一个Menu,然后通过addMenu进行添加。而添加工具栏也是雷同的步骤,也有两种方式,其一则是通过MainWindow::addToolBar()方法直接添加一个ToolBar并通过其返回的指针往ToolBar中添加Action。另一种则仍然是定义一个QToolBar的对象,然后先往其中加入需要的Action,再通过QMainWindow的addToolBar()方法往其中添加ToolBar。
3.关于QPrinter和QPrintDialog的基本使用,若需要打印文本格式的文件,那么实际上在QTextDocument中,已经有print的方法可以打印出文本,仅仅需要传入一个QPrinter的指针即可,另外QPrinterDialog是标准的打印对话框,和Qt中的对话框一样,我们可以通过传入QPrinter指针和父窗口指针来构建一个标准打印对话框窗口,并通过用户所点击的按钮判断用户点击的是哪一个按钮,并将返回值接回并判断,从而执行相关的逻辑操作。另外,对应图片的打印,我们需要明白,图片也是一种使用QPainter绘制出来的对象,只不过平时我们使用QPainter时,只是将其绘制到屏幕上进行显示,当然屏幕也是一种特殊的IO设备,那么对于打印机设备也是一样,打印机也是一种IO设备,那么我们同样可以将QPainter附加到打印机上,那么这就表示我需要将Painter绘制出来的图像绘制到打印机上,这也就是打印图片的过程。
4.对于图片的缩放或旋转操作,可以使用Qt中的QMatrix类来定义一种图像变换的规则,使用scale方法可以对图像进行放大或缩小操作。使用其中的rotate方法可以实现对图像定义一套旋转的规则。最后通过QImage的transformed方法来完成对图像的变换(该函数返回的并不是引用,因此需要单独定义一个变量将值接回,因为返回非引用类型并不会对自身进行改变)。对于图片的镜像操作,则使用到了QImage中的mirrored方法,通过设置bool值,可以实现对图片的镜像操作(该方法返回的仍然是对象变换后的实体对象而并非引用)。
本节的项目代码文件:https://files.cnblogs.com/files/blogs/792763/ImageProcessor.zip?t=1716305284&download=true
在下一节中将介绍如何实现文本的编辑功能(会涉及到QTextEdit、QTextDocument、QTextBlock、QTextList、QTextFrame、QTextTable、QTextCharFormat、QTextBlockFormat、QTextListFormat、QTextFrameFormat和QTextTableFormat的使用)。

浙公网安备 33010602011771号