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

相关推荐
风莫寻5 分钟前
【Troubleshot】Qt 长按按键 keyPressEvent keyReleaseEvent 自动重复问题
开发语言·qt
yqcoder16 分钟前
Express + MongoDB + multer 解决文件上传 originalname 中文乱码
数据库·mongodb·express
l_tian_tian_36 分钟前
JavaWeb——Mybatis、JDBC、数据库连接池、lombok
java·数据库·mybatis
m0_748247801 小时前
Python连接SQL SEVER数据库全流程
数据库·python·sql
盖盖衍上1 小时前
2-002:MySQL 索引的最左前缀匹配原则是什么?
android·数据库·mysql
cainiaojunshi1 小时前
仿Manus一
windows·microsoft
m0_555762901 小时前
通过接口或抽象类进一步解耦
数据库·系统架构
SelectDB3 小时前
SelectDB 实时分析性能突出,宝舵成本锐减与性能显著提升的双赢之旅
大数据·数据库·数据分析
V+zmm101343 小时前
电器维修系统小程序+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计
PawSQL3 小时前
推理模型对SQL理解能力的评测:DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet
java·数据库·人工智能·sql·sql优化·pawsql·deepseek