在桌面程序中,任何软件都有界面,界面就是与用户交互的,一般体现为窗口。在 QT 中通过继承 QMainWindow 来实现自定义的窗口,并为我们规划好了一个窗口大致包含的几个部分:顶部的菜单栏、底部的状态栏、外圈环绕的工具栏、内圈环绕的可悬浮的窗口部件以及最中心的中心部件。如下图:
下面就依次介绍一下 QMainWindow 中各个组件的 UI 效果及作用,这里只是简单的介绍一下,让大家有个印象,后面的文章再逐个详细的介绍。
在菜单中栏中,
MainWindow.h :
class MainWindow : public QMainWindow
{
Q_OBJECT
private slots:
void newActTriggered();
void operateActTriggered();
public:
QMenuBar *mBar;//菜单栏
QMenu *fileMenu;
QMenu *editMenu;
QAction *newAct;
QAction *operateAct;
MainWindow(QWidget *parent = 0);
void createMenus();
};
MainWindow.cpp:
本段代码主要完成在菜单栏中:
#include "mainwindow.h"
#include "qdebug.h"
void MainWindow::createMenus()
{
mBar = menuBar();//获取菜单栏
fileMenu = mBar->addMenu(tr("文件"));//创建文件菜单
editMenu = mBar->addMenu(tr("编辑"));//创建编辑菜单
//在菜单栏中添加一个 QAction 并指定 triggered() 信号的槽函数
operateAct = mBar->addAction(tr("操作"),this,SLOT(operateActTriggered()));
//在文件菜单中添加 new 菜单子项
newAct = new QAction("new");
fileMenu->addAction(newAct);
connect(this->newAct,SIGNAL(triggered()),this,SLOT(newActTriggered()));
}
//当 new 菜单子项被点击时执行
void MainWindow::newActTriggered(){
qDebug()<<"newActTriggered()";
}
//当 操作 被点击时执行
void MainWindow::operateActTriggered(){
qDebug()<<"operateTriggered()";
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
createMenus();
}
运行结果如下:
当我们点击 “new” 时就会在控制台输出:
newActTriggered()
当我们点击 “操作” 时就会在控制台输出:
operateTriggered()
我们除了可以通过 menuBar() 方法获取一个系统为我们创建好的 QMenuBar 对象外,我们也可以自己创建一个 QMenuBar 对象,再通过 setMenuBar() 将它设置到 MainWindow 中。
一般的桌面软件,在窗口界面的左下角或者最下面都是有一个状态栏的,一般显示文本编辑器中此时光标的位置,或当前文件的名称等等。在 qt 中,我们也可以给 QMainWindow 设置状态栏。
void MainWindow::createStatusBar(){
sBar = statusBar();//获取窗口的状态栏对象 QStatusBar 实例。
sBar->showMessage(tr("这里是状态栏"),0);//状态栏显示的文本
}
一般软件中,工具栏也是由一些可点击的“动作”组成的,与菜单栏不同的是,工具栏一般都是些可点击的图标,在 UI 交互上,给用户一种更加直观的感觉。
接下来我们就看一下如何用 QT 给窗口添加工具栏。
void MainWindow::showToolBar(){
fileToolBar = addToolBar(tr("文件工具"));
fileToolBar->addAction(newAct);
}
在这个案例中我们通过 QMainWindow 的 addToolBar() 方法向窗口中添加一个工具栏,然后再在窗口中添加了一个 QAction 。这个 newAct 是我们前面定义的,它一样可以被点击,并执行槽函数。
运行结果:这个我们的工具栏就是很简单的文字,大家可以给它添加一些有图标的QAction 就可以了。
并且现在这个工具栏我们可以随意拖动到窗口的下方、右方、左方。
这也就是为什么,在介绍 QMainWindow 的各个组件的时候,QToolBar 是占了一圈的原因,工具栏可以随意的放在四个方向。
我们可以通过 fileToolBar->setMovable(false); 方法将工具栏固定住,用户就不能随意拖动了。
也可通过 fileToolBar->setOrientation(Qt::Horizontal); 方法设置工具栏中的各个组件为水平排列,Qt::Vertical 为竖直方向。
通过 addToolBar(const QString &title); 默认是在上方的,且不能再设置位置了。可以通过addToolBar(Qt::ToolBarArea area, QToolBar *toolbar); 方法将一个工具栏添加到指定的位置上:
ediToolBar = new QToolBar(tr("操作工具"));
ediToolBar->addAction(operateAct);
ediToolBar->addAction(newAct);
ediToolBar->setMovable(false);
addToolBar(Qt::RightToolBarArea,ediToolBar);//添加到右边
addToolBarBreak(); 使工具栏之间添加一个间隔。
void MainWindow::showToolBar(){
fileToolBar = addToolBar(tr("文件工具"));
fileToolBar->setMovable(false);
fileToolBar->addAction(newAct);
addToolBarBreak(Qt::TopToolBarArea);
//Qt::TopToolBarArea 两个工具栏上下排列
ediToolBar = new QToolBar(tr("操作工具"));
ediToolBar->addAction(operateAct);
ediToolBar->addAction(newAct);
ediToolBar->setMovable(false);
addToolBar(ediToolBar);
}
这两个工具栏中间有一条横线间隔,且这两个工具栏是上下方向排列的。
默认情况下是左右方向的:
顾名思义,中心区域就是窗口最中心的地方,一般是用户主要操作的地方,比如,前面的 NodePad++ 的中心区域就是用户编辑文本的地方。
//将一个给定的 QWidget 作为中心区域
void QMainWindow::setCentralWidget(QWidget *widget)
//获取系统创建的中心区域
QWidget *QMainWindow::centralWidget() const
从这两个方法可以看出,只要是一个 widget 都可以作为中心区域。
简单示例:将一个 TextEdit 文本编辑框作为 centralWidget
void MainWindow::showCentralWidget(){
centerTextEdit=new QTextEdit(this);
centerTextEdit->setText(tr("这里是中心区域"));
centerTextEdit->setAlignment(Qt::AlignCenter);
setCentralWidget(centerTextEdit);
}
中间的文本编辑框就是我们的中心区域了,它在菜单栏、工具栏的下面,在状态栏的上面。
QDockWidget类提供了一个特殊的窗口部件,它可以是被锁在 QMainWindow 窗口内部或者是作为顶级窗口悬浮在桌面上。
它可以围绕在中心区域的四周,即四个方向都可以放置,与工具栏类似。
先看一个简单的示例:
void MainWindow::showDockWidget(){
dock1 = new QDockWidget(tr("dock1"),this);
dock1->setFeatures(QDockWidget::DockWidgetMovable);
dock1->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);
QTextEdit *textEdit1=new QTextEdit();
textEdit1->setText(tr("这是第一个 dockWidget,只能放在左部、右部,用户可以拖动位置"));
dock1->setWidget(textEdit1);
addDockWidget(Qt::RightDockWidgetArea,dock1);//默认在窗口的右部
dock2 = new QDockWidget(tr("dock2"),this);
dock2->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetClosable);
QTextEdit *textEdit2=new QTextEdit();
textEdit2->setText(tr("这是第二个 dockWidget 只能放在下部、右部,用户可以将它拖出窗口,浮动在桌面上;也可以关闭它"));
dock2->setWidget(textEdit2);
addDockWidget(Qt::RightDockWidgetArea,dock2);
}
上面的例子我们创建了两个 QDockWidget ,都通过 setFeatures() 设置了一些特性 :dock1 可以移动;dock2 可浮动和可关闭。
可移动就是指用户可以将 dockWidget 从原本的右部拖到左部、下部等,至于可以拖动哪个部位则由 setAllowedAreas() 来控制。
上图是将 dock1 移动到了窗口的左部。
上图是将 dock2 拖出了主窗口,让 dock2 悬浮在桌面上。因为我们给 dock2 设置了:
dock2->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetClosable);
点击右上角的关闭按钮即可关闭 dock2 ,而 dock1 没有设置可关闭特性,则不能关闭。
到这里,QMainWindow 中的各个部件就简单的介绍完了,对于每个部件更加详细的介绍就在后面的文章介绍了。比如让部件的 UI 元素更加丰富,功能更强大。
很多时候,用户在关闭软件后,会记录一下程序当前的状态,用户下次打开软件后,还是上一次退出时的状态。比如:wps 的最近打开的文件记录;对软件界面的大小设置、常用快捷键的设置等。
下面两个方法可以保存窗口信息:
QByteArray saveGeometry() const;//保存窗口大小
QByteArray saveState(int version = 0) const;//保存当前状态,QToolBar 和 QDockWidget,version 可以自己设置版本号
相反的方法就是设置状态了:
bool restoreGeometry(const QByteArray &geometry);
bool restoreState(const QByteArray &state, int version = 0);
我们拿前面的例子做一个简单示例,启动程序后,我们调整窗口的大小,并将 dock2 拖出窗口,然后关闭窗口,再次启动程序,将会是我们上一次关闭时的状态。
//点击窗口的关闭按钮时执行
void MainWindow::closeEvent(QCloseEvent *event){
QSettings settings("com.llk", "mainwindowtest");
settings.beginGroup("mainWindow");
settings.setValue("geometry", saveGeometry());
settings.setValue("windowState", saveState());
settings.endGroup();
QMainWindow::closeEvent(event);
}
void MainWindow::readSettings(){
QSettings settings("com.llk", "mainwindowtest");
settings.beginGroup("mainWindow");
//读取出保存的值,并设置到窗口中
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
settings.endGroup();
}
启动程序后,我们调整窗口的大小,再将 dock2 拖出窗口:
然后关闭软件,再启动我们的程序,还将展示上图的界面,说明程序记录了我们前一次关闭时的状态。
【注意】readSettings(); 方法的调用应在各个部件创建完成后再调用,否则没有效果。
这里我们是使用了 QSettings 类来帮我们存储窗口状态,其实我们也可以将数据保存到自己想保存的任意文件中,或是通过网络传给服务器保存都可以。只要将 saveGeometry() 和 saveState() 方法返回的字节数组的值保存起来,再次启动程序的时候再设置给 restoreGeometry() 和 restoreState() 即可。
mainwindow.h:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "qaction.h"
#include "qmenu.h"
#include "qmenubar.h"
#include "qstatusbar.h"
#include "qtoolbar.h"
#include "QDockWidget"
#include "qtextedit.h"
#include "QCloseEvent"
#include "qsettings.h"
#include "qdebug.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
private slots:
void newActTriggered();
void operateActTriggered();
public:
QMenuBar *mBar;//菜单栏
QMenu *fileMenu;
QMenu *editMenu;
QAction *newAct;
QAction *operateAct;
QStatusBar *sBar;//状态栏
QToolBar *fileToolBar;//文件工具栏
QToolBar *ediToolBar;//文件工具栏
QTextEdit *centerTextEdit;
QDockWidget *dock1;
QDockWidget *dock2;
MainWindow(QWidget *parent = 0);
void showMenus();
void showStatusBar();
void showToolBar();
void showCentralWidget();
void showDockWidget();
void closeEvent(QCloseEvent *event);
void readSettings();
~MainWindow();
};
mainwindow.cpp:
#include "mainwindow.h"
//1、菜单栏
void MainWindow::showMenus()
{
mBar = menuBar();
fileMenu = mBar->addMenu(tr("文件"));
editMenu = mBar->addMenu(tr("编辑"));
operateAct = mBar->addAction(tr("操作"),this,SLOT(operateActTriggered()));
newAct = new QAction("new");
fileMenu->addAction(newAct);
connect(this->newAct,SIGNAL(triggered()),this,SLOT(newActTriggered()));
}
//2、状态栏
void MainWindow::showStatusBar(){
sBar = statusBar();
sBar->showMessage(tr("这里是状态栏"),0);
}
//3、工具栏
void MainWindow::showToolBar(){
fileToolBar = addToolBar(tr("文件工具"));
fileToolBar->setObjectName("fileToolBar");
fileToolBar->setMovable(false);
fileToolBar->addAction(newAct);
ediToolBar = new QToolBar(tr("操作工具"));
ediToolBar->setObjectName("ediToolBar");
ediToolBar->addAction(operateAct);
ediToolBar->addAction(newAct);
ediToolBar->setMovable(false);
addToolBar(ediToolBar);
}
//4、中心区域
void MainWindow::showCentralWidget(){
centerTextEdit=new QTextEdit(this);
centerTextEdit->setText(tr("这里是中心区域"));
centerTextEdit->setAlignment(Qt::AlignCenter);
setCentralWidget(centerTextEdit);
}
//5、悬浮部件
void MainWindow::showDockWidget(){
dock1 = new QDockWidget(tr("dock1"),this);
dock1->setObjectName("dock1");
dock1->setFeatures(QDockWidget::DockWidgetMovable);
dock1->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea);
QTextEdit *textEdit1=new QTextEdit();
textEdit1->setText(tr("这是第一个 dockWidget,只能放在左部、右部,用户可以拖动位置"));
dock1->setWidget(textEdit1);
addDockWidget(Qt::RightDockWidgetArea,dock1);//默认在窗口的右部
dock2 = new QDockWidget(tr("dock2"),this);
dock2->setObjectName("dock2");
dock2->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetClosable);
dock2->setAllowedAreas(Qt::BottomDockWidgetArea|Qt::RightDockWidgetArea);
QTextEdit *textEdit2=new QTextEdit();
textEdit2->setText(tr("这是第二个 dockWidget 只能放在下部、右部,用户可以将它拖出窗口,浮动在桌面上;也可以关闭它"));
dock2->setWidget(textEdit2);
addDockWidget(Qt::RightDockWidgetArea,dock2);
}
void MainWindow::closeEvent(QCloseEvent *event){
QSettings settings("com.llk", "mainwindowtest");
settings.beginGroup("mainWindow");
settings.setValue("geometry", saveGeometry());
settings.setValue("windowState", saveState());
settings.endGroup();
QMainWindow::closeEvent(event);
}
void MainWindow::readSettings(){
QSettings settings("com.llk", "mainwindowtest");
settings.beginGroup("mainWindow");
restoreGeometry(settings.value("geometry").toByteArray());
restoreState(settings.value("windowState").toByteArray());
settings.endGroup();
}
void MainWindow::newActTriggered(){
qDebug()<<"newActTriggered()";
}
void MainWindow::operateActTriggered(){
qDebug()<<"operateTriggered()";
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
showMenus();
showStatusBar();
showToolBar();
showCentralWidget();
showDockWidget();
readSettings();
}
MainWindow::~MainWindow()
{
}
main.cpp:
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}