(7)框架搭建:Qt实战项目之主窗体导航栏、状态栏

上一章节我们已经掌握了 Qt 工程项目的创建与基础界面元素(菜单栏、工具条)的搭建,这些组件为应用程序提供了核心操作入口。本节课将在此基础上,进一步完善界面布局 ------ 导航工具条能让用户快速切换功能模块,状态栏则用于展示关键状态信息,二者结合将大幅提升应用的实用性和用户体验。下面我们通过实战案例,详细讲解这两个组件的搭建流程。

核心类介绍

本章节中介绍的导航条依然采用工具条进行构建,本节新学的核心类如下:
1. QLabel 类

QLabel是 Qt 最基础的 "内容显示类",支持文本、图片、HTML 内容展示,在状态栏中主要用于承载静态文本(如版本号)和动态更新的内容(如当前时间)。

2. QStatusBar 类

QStatusBar是主窗口底部的 "状态信息管理类",专门用于展示临时提示、永久信息或动态状态,是应用程序与用户的 "信息反馈窗口"。

3.QDateTime类

QDateTime是 Qt 提供的 "日期时间处理类",用于获取系统当前日期时间、对日期时间进行计算(如增减时间)和格式化输出,是实现状态栏 "动态时间显示" 功能的核心依赖。

工具条实战

实战内容

本节课的实战内容是在上一节课的基础上,再创建一个导航工具条,以及创建一个状态栏,要求如下:

1、导航工具条上放置两个按钮,分别是"建模"、"拓扑",代表两个不同的功能模块;

2、导航工具条上的功能按钮有两个状态,选中、未选中时要有区分;

3、工具条上的按钮以图标的形式显示,并且在图标下放显示出操作的名称;

4、工具条默认显示在窗口左侧位置,工具条禁止移动到其他的区域;

5、状态栏中左侧显示临时信息,右侧展示当前时间;

实战步骤

头文件引入

根据本节课需要用到的Qt类,我们需要引入以下头文件

cpp 复制代码
#include <QLabel>
#include <QStatusBar>
#include <QDateTime>

同样,我们分别定义两个单独的函数来实现导航工具条和状态栏的逻辑,函数分别定义为:

cpp 复制代码
void createNaviToolBar();// 导航工具条
void createStatusBar();  // 状态栏

createNaviToolBar()的实现

通过该函数实现工具条的创建、按钮的添加以及状态的切换。根据上节课学习的搭建工具条的方式创建一个导航的工具条,样式同样设置为图标在上、文字在下的样式。但是要注意的我们将工具栏的位置设置在窗口左侧,同时禁止工具条移动,可以通过setMovable设置工具条是不可移动的。设置了该属性值为false,则工具条不允许拖拽移动。

cpp 复制代码
QToolBar* naviToolBar = addToolBar(QStringLiteral("导航条"));
addToolBar(Qt::LeftToolBarArea, naviToolBar);
naviToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
naviToolBar->setMovable(false);

我们再给工具条定义一个标题,这里我们要用到QLabel类,QLabel 是 Qt 框架中用于显示文本或图像的基础控件,继承自 QFrame。它通常用于显示静态文本、图片、链接或简单的富文本内容,是界面设计中最常用的组件之一。我们在整个开发中经常会用到该类,也会用到该类的不同用法,此处只是用来显示工具条的标题文本。

我们先创建一个QLabel对象,然后通过QToolBar的addWidget方法将其添加到工具栏上。QToolBar::addWidget(QWidget *widget) 方法的作用是将一个任意的 Qt 控件添加到工具栏中。这个方法会将传入的控件作为工具栏的一部分显示,并按照添加顺序排列在工具栏上。

cpp 复制代码
QLabel* titleLabel = new QLabel("导航", naviToolBar);
naviToolBar->addWidget(titleLabel);

依次添加"建模"、"拓扑"个菜单项,按照前面课程学到的QAction的定义方法进行定义和添加,但要注意因为我们需要用到菜单项状态的切换,所以每个按钮找两个不同颜色的图片以区分状态。先把图标放到我们前面定义的image目录下,在添加到资源qrc中(按照前面课程的内容自行添加,)。

前面的课程中定义快捷工具条我们学到的是先创建一个QAction对象,然后通过setIcon函数为其设置图片,这节课我们使用QAction的另外一个构造函数:

++QAction(const QIcon &icon , const QString &text , QObject *parent = nullptr)++

通过这个构造函数可以直接创建一个带图标的按钮,让后加入到导航菜单中。 这里关键的一点是,第一个参数的QIcon图标需要有两个状态,我们会用到QIcon的一个方法:

++addPixmap(const QPixmap &pixmap, QIcon::Mode mode = Normal, QIcon::State state = Off)。++

  • 第一个参数 pixmap:表示当前要添加的图片资源(QPixmap 对象),即这个图片会在特定的 mode 和 state 下显示。例如,选中状态的图标图片、未选中状态的图标图片等。
  • 第二个参数 mode:表示图标所处的「模式」,由 QIcon::Mode 枚举定义,常用取值:

QIcon::Normal:正常模式(默认值),控件处于启用且未被选中 /hover 的状态。

QIcon::Disabled:禁用模式,控件被禁用(setEnabled(false))时显示。

QIcon::Active:激活模式,鼠标 hover 或聚焦到控件时显示。

QIcon::Selected:选中模式,某些控件(如菜单项)被选中时显示(较少用,通常用 State 区分选中)。

  • 第三个参数 state:表示图标所处的「激活状态」,由 QIcon::State 枚举定义,取值:

QIcon::Off:未激活(默认值,如按钮未选中)。

QIcon::On:激活(如按钮选中)。

第二个参数我们保持QIcon::Normal即可,如果将来需要处理禁用、hover 等状态,再指定对应模式,我们分别定义激活、未激活状态。按照上面的逻辑我们先定义两个QIcon对象。然后创建QAction,将QAction对象添加的导航栏,并设置其为可点击。

cpp 复制代码
// 添加菜单项
QIcon modelIcon, topologyIcon;
modelIcon.addPixmap(QPixmap(":/SubCfgTool/image/model.png"), QIcon::Normal, QIcon::Off);
modelIcon.addPixmap(QPixmap(":/SubCfgTool/image/model_sel.png"), QIcon::Normal, QIcon::On);
topologyIcon.addPixmap(QPixmap(":/SubCfgTool/image/topology.png"), QIcon::Normal, QIcon::Off);
topologyIcon.addPixmap(QPixmap(":/SubCfgTool/image/topology_sel.png"), QIcon::Normal, QIcon::On);

QAction* modelAct = new QAction(modelIcon, QStringLiteral("建模"), this);
QAction* topologyAct = new QAction(topologyIcon, QStringLiteral("拓扑"), this);
naviToolBar->addAction(modelAct);
naviToolBar->addAction(topologyAct);

// 设置菜单具备可点击状态
modelAct->setCheckable(true);
topologyAct->setCheckable(true);

将createNaviToolBar()函数放入到构造函数中调用,然后编译运行测试一下效果。

从测试效果来看,按钮可以点击并切换状态,但是两个按钮没有互斥,即同一时间两个按钮都可以被选中,后面我们学到信号与槽的机制时再优化这部分内容。还有个问题是整个页面的布局比较紧凑,需要调整一下布局的样式,这里引入一个新的成员函数setStyleSheet,这个函数是QToolBar继承自QWidget的函数。setStyleSheet 是 Qt 框架中用于设置控件外观的核心函数,可以参考Qt Style Sheets官方文档。

每个界面组件都可以用上面的模型来表示。

CONTENT属性:是显示内容矩形区域,如QLabel用于显示文字的区域。min-width、max-width、min-height和max-height属性定义最大/最小宽度或高度,就是定义这个矩形区。

PADDING属性:是包围content的矩形区域,通过padding属性可以定义padding的宽度,padding-top、padding-bottom、padding-left、padding-right分别定义padding的上下左右。

BORDER属性:是包围padding的边框,通过border属性(border-width、border-style、border-color)可以定义边框的线宽、、线型和颜色,也可以分别定义border的上、下、左、右的线宽和颜色。

MARGIN属性:是border之外的父组件之间的空白边距,可以分别定义上、下、左、右的边距大小。

对于导航工具条的布局我们做两个调整,首先是将"建模"、"拓扑"这两个菜单项的Padding加大,让菜单项四周有一些留白,同时我们加大菜单项之间的间距,用到了spacing属性,这属于布局的范围,后面讲到布局会详细讲解,这里只需要按代码实现即可。

cpp 复制代码
 naviToolBar->setStyleSheet("padding:2px;spacing:16px;");

createStatusBar()的实现

因为QMainWindow创建的时候已经为我们创建了状态栏,所以我们只需要定义一个QStatusBar对象,通过QMainWindow的statusBar()方法返回 QStatusBar的对象。

cpp 复制代码
 QStatusBar* statusBar = this->statusBar();

这里会涉及两个重要的方法:

  • **addWidget():**添加左侧控件,受临时信息影响,适合非永久内容。
  • **addPermanentWidget():**添加右侧永久控件,不受临时信息影响,适合固定内容。

下面我们在状态栏的左侧添加一个状态信息展示标签、在状态栏的右侧加一个时间显示。

定义一个全局的QLabel,命名为QLabel* StatusInfoLbl,用于显示状态信息。

cpp 复制代码
    StatusInfoLbl = new QLabel(QStringLiteral("已就绪"));
    statusBar->addWidget(StatusInfoLbl);

定义一个QLabel,用于显示时间,并通过addPermanentWidget函数添加至状态栏,默认显示在右侧。 因为还没有学到信号和槽,我们先不做时间的更新,只显示个当前的时间即可。

cpp 复制代码
// 创建时间标签
QLabel* timeLabel = new QLabel(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));

// 直接添加到状态栏右侧(无需伸缩控件)
statusBar->addPermanentWidget(timeLabel);

将createStatusBar()函数放入构造函数中调用,并测试运行,效果如下:

本章小结

本节课的内容是 Qt 界面开发的重要延伸,导航工具条解决了多功能模块的快速切换问题,状态栏实现了用户操作反馈与关键信息展示,二者结合大幅提升了应用的实用性与用户体验。这类布局在各类桌面应用(如编辑器、设计软件、管理工具)中广泛应用,掌握后可快速搭建结构清晰、交互友好的基础界面框架。同时,通过本节课的学习,进一步熟悉了 Qt 控件的属性配置、资源管理与样式调整方法,为后续信号与槽、动态交互等进阶内容打下了坚实基础。

附录

SubCfgTool.h代码:

cpp 复制代码
#pragma once

#include <QtWidgets/QMainWindow>
//#include "ui_SubCfgTool.h"
#include <QMenuBar>
#include <QMenu>
#include <QAction>
#include <QToolBar>
#include <QLabel>
#include <QStatusBar>
#include <QDateTime>

class SubCfgTool : public QMainWindow
{
    Q_OBJECT

public:
    SubCfgTool(QWidget *parent = nullptr);
    ~SubCfgTool();

    void createMenuBar();
    void createToolBar();
    void createNaviToolBar();
    void createStatusBar();

private:
	QAction* createProjAct;
	QAction* openProjAct;
    QAction* quitAct;

    QLabel* StatusInfoLbl;
};

SubCfgTool.cpp代码:

cpp 复制代码
#include "SubCfgTool.h"

SubCfgTool::SubCfgTool(QWidget *parent)
    : QMainWindow(parent)
{
   // ui.setupUi(this);
	createMenuBar();
	createToolBar();
	createNaviToolBar();
	createStatusBar();
}

SubCfgTool::~SubCfgTool()
{}

void SubCfgTool::createMenuBar()
{
	// 菜单栏
	QMenuBar* menuBar = this->menuBar();
	// 菜单
	QMenu* fileMenu = menuBar->addMenu("文件");
	QMenu* helpMenu = menuBar->addMenu("帮助");

	// 定义文件菜单中的菜单项
	createProjAct = new QAction("新建项目");
	openProjAct = new QAction("打开项目");
	quitAct = new QAction("退出");
	// 将菜单项加入到文件菜单
	fileMenu->addAction(createProjAct);
	fileMenu->addAction(openProjAct);
	fileMenu->addSeparator();
	fileMenu->addAction(quitAct);

	// 定义帮助菜单中的菜单项
	QAction* aboutAct = new QAction("关于");
	// 将菜单加入到帮助菜单项
	helpMenu->addAction(aboutAct);

	// 添加快捷键
	createProjAct->setShortcut(QKeySequence::New);
	openProjAct->setShortcuts(QKeySequence::Open);
	createProjAct->setAutoRepeat(false);

	// 临时测试代码
	connect(createProjAct, &QAction::triggered, []() {qDebug() << "Create New Project"; });


	// 设置图标
	createProjAct->setIcon(QIcon(":/SubCfgTool/image/menu_new.png"));
	openProjAct->setIcon(QIcon(":/SubCfgTool/image/menu_open.png"));

}

void SubCfgTool::createToolBar()
{
	QToolBar* mainToolBar = new QToolBar(QStringLiteral("工具条"));
	mainToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
	addToolBar(Qt::TopToolBarArea, mainToolBar);

	mainToolBar->addAction(createProjAct);
	mainToolBar->addAction(openProjAct);
	mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
}

void SubCfgTool::createNaviToolBar()
{
	// 创建导航条
	QToolBar* naviToolBar = addToolBar(QStringLiteral("导航条"));
	addToolBar(Qt::LeftToolBarArea, naviToolBar);
	naviToolBar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
	naviToolBar->setMovable(false);
	// 添加标题
	QLabel* titleLabel = new QLabel("导航", naviToolBar);
	naviToolBar->addWidget(titleLabel);

	// 添加菜单项
	QIcon modelIcon, topologyIcon;
	modelIcon.addPixmap(QPixmap(":/SubCfgTool/image/model.png"), QIcon::Normal, QIcon::Off);
	modelIcon.addPixmap(QPixmap(":/SubCfgTool/image/model_sel.png"), QIcon::Normal, QIcon::On);
	topologyIcon.addPixmap(QPixmap(":/SubCfgTool/image/topology.png"), QIcon::Normal, QIcon::Off);
	topologyIcon.addPixmap(QPixmap(":/SubCfgTool/image/topology_sel.png"), QIcon::Normal, QIcon::On);

	QAction* modelAct = new QAction(modelIcon, QStringLiteral("建模"), this);
	QAction* topologyAct = new QAction(topologyIcon, QStringLiteral("拓扑"), this);
	naviToolBar->addAction(modelAct);
	naviToolBar->addAction(topologyAct);

	// 设置菜单具备可点击状态
	modelAct->setCheckable(true);
	topologyAct->setCheckable(true);

	naviToolBar->setStyleSheet("padding:2px;spacing:16px;");
	


}

void SubCfgTool::createStatusBar()
{
	QStatusBar* statusBar = this->statusBar();

	// 左侧状态信息
	StatusInfoLbl = new QLabel(QStringLiteral("已就绪"));
	statusBar->addWidget(StatusInfoLbl);

	// 右侧时间区域
	// 创建时间标签
	QLabel* timeLabel = new QLabel(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));

	// 直接添加到状态栏右侧(无需伸缩控件)
	statusBar->addPermanentWidget(timeLabel);
}
相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00616 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术16 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript