Qt 实战(9)窗体 | 9.1、QWidget

文章目录

前言:

在Qt这一强大的跨平台C++图形用户界面应用程序开发框架中,QWidget扮演着至关重要的角色。作为所有用户界面对象的基类,QWidget不仅提供了窗口系统的基本功能,还是构建复杂GUI应用程序的基石。本文将深入探讨QWidget的基本概念,并重点介绍其常用的特性。

一、QWidget

1、定义与概念

QWidget是Qt中所有用户界面对象的基类,包括窗口、对话框、按钮、文本框等。它封装了窗口系统的很多特性,如大小、位置、显示状态等,并提供了一系列用于这些特性的方法和信号槽。通过继承QWidget,开发者可以创建自定义的用户界面元素。

2、继承体系

QWidget继承自QObjectQPaintDevice。这意味着它不仅具有Qt对象系统的基本特性(如信号槽机制、属性系统、事件处理等),还能够进行绘图操作。同时,QWidget还是许多其他Qt控件(如QPushButtonQLabelQMainWindow等)的基类。

3、常用特性

3.1、事件处理

QWidget支持事件处理机制,允许开发者对用户的操作(如鼠标点击、键盘输入)或其他系统事件(如窗口大小改变)进行响应。通过重写事件处理函数(如mousePressEventkeyPressEvent等),可以实现复杂的交互逻辑。下面是QWidget提供的事件处理器,如下:

cpp 复制代码
// Event handlers
// 鼠标事件
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseDoubleClickEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);

// 键盘事件
virtual void keyPressEvent(QKeyEvent *event);
virtual void keyReleaseEvent(QKeyEvent *event);

// 焦点事件
virtual void focusInEvent(QFocusEvent *event);
virtual void focusOutEvent(QFocusEvent *event);
virtual void enterEvent(QEvent *event);
virtual void leaveEvent(QEvent *event);

// 绘制事件
virtual void paintEvent(QPaintEvent *event);

// 窗体关闭事件
virtual void closeEvent(QCloseEvent *event);

3.2、布局管理

为了确保界面元素在不同分辨率和窗口大小下都能保持良好的布局,Qt提供了多种布局管理器(如QVBoxLayoutQHBoxLayoutQGridLayout等)。这些布局管理器可以与QWidget结合使用,实现自动的布局调整。下面是QWidget提供的与布局管理相关的成员函数,如下:

cpp 复制代码
QLayout *layout() const;
void setLayout(QLayout *);

3.3、子控件与父子关系

QWidget支持子控件的概念,即一个QWidget可以包含其他QWidget作为其子控件。这种父子关系不仅简化了界面元素的组织方式,还便于事件处理和布局管理。

cpp 复制代码
void setParent(QWidget *parent);
void setParent(QWidget *parent, Qt::WindowFlags f);

static QWidget *find(WId);

3.4、右键菜单

QWidget提供相关的接口可以设置自定义右键菜单,如下:

cpp 复制代码
void QWidget::setContextMenuPolicy(Qt::ContextMenuPolicy policy)
枚举值 作用
Qt::NoContextMenu 本对象不显示右键菜单,由父对象决定
Qt::PreventContextMenu 不显示右键菜单,与NoContentMenu不同,不向上层传递右键菜单的命令
Qt::DefaultContextMenu 调用QWidget::contextMenuEvent(), 子类如果自定义菜单项需要重写这个虚函数 。(注意:该值是默认的属性值)
Qt::ActionsContextMenu 将本QWidget的所有QAction作为右键显示的菜单,这些QAction是通过QWidget::addActions方法添加的
Qt::CustomContextMenu 发送信号QWidget::customContextMenuRequested(), 通过发信号的形式

下面通过一个示例介绍常用的几种策略的用法,如下:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

#include <QDebug>
#include <QAction>

#define ACTION_MAX_SIZE 5

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    m_pMenu = nullptr;
    m_pCustomMenu = nullptr;

    ui->comboBox->addItem("Qt::DefaultContextMenu");
    ui->comboBox->addItem("Qt::ActionsContextMenu");
    ui->comboBox->addItem("Qt::CustomContextMenu");

    // 方法一:给Widget添加右键菜单,如果右键菜单策略设置的是Qt::ActionsContextMenu,右键时会显示Widget所有的Actions
    QList<QAction*> actionList;
    for (int i = 0; i < ACTION_MAX_SIZE; i ++)
    {
        QAction *pAction = new QAction(this);
        pAction->setText("ActionsContextMenu Action " + QString::number(i));
        connect(pAction, SIGNAL(triggered(bool)), this, SLOT(OnAction()));
        actionList.append(pAction);
    }

    // 调用QWidget::addActions方法添加QAction,当策略采用的是Qt::ActionsContextMenu,会显示添加的菜单
    addActions(actionList);

    connect(ui->comboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(OnCurrentIndexChange(int)));

    // 方法二:通过连接QWidget::customContextMenuRequested信号,实现右键菜单功能,当策略采用的是Qt::CustomContextMenu,会显示槽函数里面的菜单
    connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(OnShowMenu(const QPoint &)));
}

Widget::~Widget()
{
    delete ui;

    if (m_pMenu)
    {
        delete m_pMenu;
    }
}

// 方法三:如果右键菜单策略选择的是Qt::DefaultContextMenu(默认的策略),QWidget的派生类通过实现虚函数contextMenuEvent
// 来弹出右键菜单
void Widget::contextMenuEvent(QContextMenuEvent *event)
{
    if (m_pMenu == nullptr)
    {
        m_pMenu = new QMenu(this);
        m_pMenu->setParent(this);

        QList<QAction*> actionList;
        for (int i = 0; i < ACTION_MAX_SIZE; i ++)
        {
            QAction *pAction = new QAction(this);
            pAction->setText("DefaultContextMenu Action " + QString::number(i));
            connect(pAction, SIGNAL(triggered(bool)), this, SLOT(OnAction()));
            actionList.append(pAction);
        }

        m_pMenu->addActions(actionList);
    }


    m_pMenu->move(event->globalX() ,event->globalY());
    m_pMenu->show();
}

void Widget::OnAction()
{
    QObject *pObj = sender();
    QAction *pAction = dynamic_cast<QAction*>(pObj);

    qDebug() << "Clieck " << pAction->text();
}

void Widget::OnCurrentIndexChange(int index)
{
    switch(index)
    {
    case 0:
        setContextMenuPolicy(Qt::DefaultContextMenu);
        ui->label->setText("MenuPolicy Qt::DefaultContextMenu");
        break;
    case 1:
        setContextMenuPolicy(Qt::ActionsContextMenu);
        ui->label->setText("Qt::ActionsContextMenu");
        break;
    case 2:
        setContextMenuPolicy(Qt::CustomContextMenu);
        ui->label->setText("Qt::CustomContextMenu");
        break;
    }
}

void Widget::OnShowMenu(const QPoint &pos)
{
    if (m_pCustomMenu == nullptr)
    {
        m_pCustomMenu = new QMenu(this);
        m_pCustomMenu->setParent(this);

        QList<QAction*> actionList;
        for (int i = 0; i < ACTION_MAX_SIZE; i ++)
        {
            QAction *pAction = new QAction(this);
            pAction->setText("CustomContextMenu Action " + QString::number(i));
            connect(pAction, SIGNAL(triggered(bool)), this, SLOT(OnAction()));
            actionList.append(pAction);
        }

        m_pCustomMenu->addActions(actionList);
    }

    m_pCustomMenu->move(pos);
    m_pCustomMenu->show();
}

运行结果

4、注意事项

QWidget使用过程,应该注意下面这些问题,如下:

  • 内存管理 :在Qt中,QWidget及其子类对象的生命周期通常与父对象相关联。当父对象被销毁时,其所有子对象也会被自动销毁。因此,在创建QWidget对象时,需要注意其父子关系的设置,以避免内存泄漏。
  • 事件处理:虽然Qt的事件处理机制非常强大和灵活,但在重写事件处理函数时仍需谨慎。错误的事件处理逻辑可能导致界面响应缓慢、卡顿甚至崩溃。
  • 布局管理:合理使用布局管理器可以极大地简化界面布局的工作,并提升用户体验。然而,在复杂的应用程序中,布局管理也可能变得相当复杂和耗时。因此,在设计界面布局时,需要仔细考虑和规划。

5、总结

QWidget是Qt框架中不可或缺的一部分,它为开发者提供了构建复杂GUI的强大工具和灵活机制。通过深入理解和掌握QWidget及其相关类,开发者可以创建出功能丰富、界面美观的桌面应用程序。

相关推荐
IvorySQL18 分钟前
PostgreSQL 性能:云端与本地的延迟分析
数据库·postgresql
wangbing112518 分钟前
分组取前几位
数据库
给我来一根41 分钟前
用户认证与授权:使用JWT保护你的API
jvm·数据库·python
_F_y1 小时前
MySQL表的操作
android·数据库·mysql
SmartBrain1 小时前
Agent 知识总结
服务器·数据库·笔记
fenglllle2 小时前
MySQL explain format的差异
数据库·mysql
look ahead to2 小时前
关于PYQT qt designer的网格布局 单控件占多行的处理
开发语言·qt·pyqt
哈哈不让取名字2 小时前
用Pygame开发你的第一个小游戏
jvm·数据库·python
程序员敲代码吗2 小时前
Python异步编程入门:Asyncio库的使用
jvm·数据库·python
志凌海纳SmartX3 小时前
榫卯企业云平台:让企业自建云更简单
数据库