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及其相关类,开发者可以创建出功能丰富、界面美观的桌面应用程序。

相关推荐
斯特凡今天也很帅4 分钟前
clickhouse常用语句汇总——持续更新中
数据库·sql·clickhouse
超级小忍1 小时前
如何配置 MySQL 允许远程连接
数据库·mysql·adb
吹牛不交税2 小时前
sqlsugar WhereIF条件的大于等于和等于查出来的坑
数据库·mysql
hshpy2 小时前
setting up Activiti BPMN Workflow Engine with Spring Boot
数据库·spring boot·后端
文牧之3 小时前
Oracle 审计参数:AUDIT_TRAIL 和 AUDIT_SYS_OPERATIONS
运维·数据库·oracle
篱笆院的狗3 小时前
如何使用 Redis 快速实现布隆过滤器?
数据库·redis·缓存
PT_silver4 小时前
tryhackme——Abusing Windows Internals(进程注入)
windows·microsoft
洛神灬殇4 小时前
【LLM大模型技术专题】「入门到精通系列教程」基于ai-openai-spring-boot-starter集成开发实战指南
网络·数据库·微服务·云原生·架构
小鸡脚来咯4 小时前
redis分片集群架构
数据库·redis·架构
christine-rr5 小时前
征文投稿:如何写一份实用的技术文档?——以软件配置为例
运维·前端·网络·数据库·软件构建