【Qt6】QWidgetAction 的使用

这篇具有很好参考价值的文章主要介绍了【Qt6】QWidgetAction 的使用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在开始主题前,先看一个 C++ 例子:

#include <iostream>

struct Data
{
    int a;
    int b;
};

// 注意这里
struct Data *s;

void doSome()
{
    Data k;
    k.a = 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

int main()
{
    // 先调用了函数
    doSome();
    // 再输出 Data 结构体的内容
    std::cout << "a = " << s->a << '\n';
    std::cout << "b = " << s->b << '\n';
    return 0;
}

不要问这个例子的功能,问就是超能力。其实这个例子没啥功能,纯粹是为了运行后出错而写的。有同学会疑惑:这程序好像没啥问题。嗯,看着是没啥问题,我们预期的情况是:a 的值是 100,b 的值是 300。

遗憾的是,运行结果是这样的:

a = -858993460
b = -858993460

啥玩意儿?下面咱们就扒一下到底哪里出事了。

这个例子先定义了一个结构体叫 Data,里面有两个字段 a、b。然后声明 Data 类型的指针变量,在 doSome 函数中让变量 s 引用了一个 Data 实例的实例。在 main 函数中,先调用 doSome 函数,然后再输出 a、b 的值。这里就出现一个问题了:s 引用的 k 是在 doSome 函数内创建的,而且它的数据分配在栈上,当 doSome 函数执行结束时,k 的生命周期也差不多了。当调用 doSome 函数之后访问 s,此时 s 所指向的对象已经没有了,所以 a、b 输出的是一个“脏”的值。

若是把 k 改为 static,那结果就不一样了。

void doSome()
{
    static Data k;
    k.a = 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

控制台将输出:

a = 100
b = 300

如果你不相信上述现象,也可以把例子改成这样:

#include <iostream>

class Test
{
public:
    Test()
    {
        std::cout << "Test 构造函数 ..." << std::endl;
    }

    ~Test()
    {
        std::cout << "Test 析构函数 ..." << std::endl;
    }
    int a,b;
};

// 注意这里
Test *s;

void doSome()
{
    Test k;
    k.a= 100;
    k.b = 300;
    // 注意这里,会出大事
    s = &k;
}

int main()
{
    // 先调用了函数
    std::cout << "调用doSome函数前\n";
        doSome();
    std::cout << "调用doSome函数后\n";
    // 再输出a、b的内容
    std::cout << "a = " << s->a << '\n';
    std::cout << "b = " << s->b << '\n';
    return 0;
}

运行上述代码,得到的输出为:

Test 构造函数 ...
Test 析构函数 ...
调用doSome函数后
a = -858993460
b = -858993460

这样就能清楚地知道,s 引用的对象在退出 doSome 函数之前就已经析构了。除了使用 static 关键字外,也可以让 Test 对象分配在堆上。

void doSome()
{
    Test *k = new Test;
    k->a = 100;
    k->b = 300;
    // 复制的是地址,不是对象
    s = k;
}

把 k 赋值给 s,只是把指向的地址复制一遍罢了,对象实例并没有复制。栈上的数据会因变量的生命周期而被回收,但堆上的东西需要 delete。所以,在调用完 doSome 函数后,堆上的东西还在,所以输出的 a、b 值不会“脏”。按理说,s 用完了应该 delete 的,不过,我没写 delete 语句,毕竟这里 main 函数马上就执行完了,程序都结束了,堆上的东西早没了,所以,这里就偷偷懒吧,不必管它。

下面再来看一个 Qt 程序:

#include <QWidget>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>


int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    // 创建两个按钮
    QPushButton btnA("Yes");
    QPushButton btnB("No");
    // 创建顶层窗口
    QWidget window;
    
    // 构建对象树
    btnA.setParent(&window);
    btnB.setParent(&window);
    // 设置按钮在窗口中的位置
    btnA.move(28, 30);
    btnB.move(28, 75);

    // 显示窗口
    window.show();

    return QApplication::exec();
}

上述程序也是一个有问题的程序,但它能运行,只是在关闭窗口时报错。

Unhandled exception at 0x00007FFDD029C1F9 (ntdll.dll) in myapp.exe: 0xC0000374: 堆已损坏。 (parameters: 0x00007FFDD03118A0).

这个问题和第一个例子的有点像但又不完全一样。这个 Qt 程序是一个经典错误,问题出在两个 QPushButton 对象被析构了两次。由于所有变量都是在栈上分配的,上述程序的压入顺序是 btnA - btnB - window。按照后进先出的规则,window 变量是最新定义的,它首先发生析构。由于 btnA、btnB 调用了 setParent 方法设置了对象树关系,当 window 析构时会删除 btnA、btnB。又因变量生命周期的原因,在 window 析构之后,btnA 和 btnB 又发生析构(可刚才 window 让它们析构过了)。

解决方法:1、调整声明变量的顺序,先声明 window 变量,再声明其他变量;2、用指针。

下面代码改为用指针类型。

#include <QWidget>
#include <QApplication>
#include <QVBoxLayout>
#include <QPushButton>


int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    // 创建两个按钮
    QPushButton *btnA = new QPushButton("Yes");
    QPushButton *btnB = new QPushButton("No");
    // 创建顶层窗口
    QWidget *window = new QWidget;
    
    // 构建对象树
    btnA->setParent(window);
    btnB->setParent(window);
    // 设置按钮在窗口中的位置
    btnA->move(28, 30);
    btnB->move(28, 75);

    // 显示窗口
    window->show();

    return QApplication::exec();
}

这里咱们也不需要 delete,毕竟窗口和两个按钮在应用程序运行期间它们都必须存在的,只到了程序退出时才销毁,那就没必要 delete 了。

所以说:

1、不是所有指针变量都要 delete 的,因为它引用的可能不是堆上的对象,没准是栈上的对象;

2、不是所有 new 出来的对象就非要 delete 不可,主要看它的生命周期是否该结束。如果是短暂使用的,在应用程序运行期间不需要一直存在的,用完就要 delete。有些 new 出来的对象可能要传递给其他对象用,并由它们负责释放,那也不需要 delete,比如包装剪贴板数据的 QMimeData 类。

==========================================================================

好了,以上一大段内容就当作科普,正片现在才开始。本篇咱们看一下特殊的 QAction 类——QWidgetAction。看名字也可以联想到,它是可以把一个 QWidget 用作 action 的类。这个有什么用呢?作用就是你可以在菜单里做些交互功能。

 QWidgetAction 类有两种用法:

1、直接用,这是最简单方法。实例化后调用 setDefaultWidget 方法设置一个 widget;

2、派生出子类,重写 createWidget 方法,创建你需要的组件对象。

先看第一种用法,非常好办,你想在菜单项上显示什么组件就创建它,然后调用 setDefaultWidget 方法就行了。

// 头文件
#ifndef APP_H
#define APP_H

#include <QMainWindow>
#include <QWidget>
#include <QAction>
#include <QSpinBox>
#include <QMenu>
#include <QMenuBar>
#include <QWidgetAction>

class MyWindow : public QMainWindow
{
public:
    MyWindow();
};

#endif
/*---------------------------------------------*/
// 代码文件
MyWindow::MyWindow()
    :QMainWindow((QWidget*)nullptr)
{
    // 创建菜单栏
    QMenuBar *menubar = this->menuBar();
    // 创建菜单
    QMenu *menu = menubar->addMenu("应用程序");
    // 添加两个普通action,意思一下
    menu->addAction("打开文件");
    menu->addAction("关闭文件");
    // 下面才是主角
    QWidgetAction *widgetAct = new QWidgetAction(menu);
    // 创建一个数字组件
    QSpinBox *spinbox = new QSpinBox;
    // 设置一下有效范围
    spinbox->setRange(0, 1000);
    // 设置当前值
    spinbox->setValue(250);
    // 设置为 QWidgetAction 的默认组件
    widgetAct->setDefaultWidget(spinbox);
    // 把action添加到菜单中
    menu->addAction(widgetAct);
}

应用程序窗口继承了 QMainWindow 类,因为这个类比较方便构建菜单栏、工具栏、状态栏、停靠栏。咱们用它来创建一个菜单栏对象(QMenuBar),然后添加一个叫“应用程序”的菜单(QMenu)。

“应用程序”菜单的前两个菜单项是普通的 action,第三个是 QWidgetAction 对象。在 new 出 QWidgetAction 后,先初始化一下 QSpinBox 组件,然后调用 setDefaultWidget 方法,这样 QSpinBox 组件就能显示在菜单项上了。

在 main 函数中显示主窗口。

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    MyWindow *win = new MyWindow;
    win->setWindowTitle("自定义菜单项");
    win->resize(450, 400);
    win->show();
    return QApplication::exec();
}

好了,见证奇迹的时候到了,看看效果。

【Qt6】QWidgetAction 的使用

 

另一种用法,就是从 QWidgetAction 类派生。然后重写这个方法:

QWidget *createWidget(QWidget *parent);

parent 是父级对象,由调用者传递,这取决于这个自定义的 action 用在什么容器上了,如果用在菜单上,就是 QMenu 对象。返回值就是创建的自定义组件了。

另外,如果在析构自定义组件时有特殊处理,还可以重写 delete 方法。

void deleteWidget(QWidget *widget);

widget 参数是要被删除的自定义组件实例。如果无其他要实现的需求,没必要重写它。

下面咱们来个示例:自定义组件做个带三个滑块的界面。组件名称为 CustWidget,基类是 QFrame。选择 QFrame 作为基类是方便设置边框。

// 头文件
#ifndef CUSTWIDGET_H
#define CUSTWIDGET_H
#include <QWidget>
#include <QFrame>

class CustWidget: public QFrame
{
public:
    CustWidget(QWidget* parent = nullptr);
private:
    void initUI();
};
#endif

// 代码文件
#include "custWidget.h"
#include <QFormLayout>
#include <QSlider>

CustWidget::CustWidget(QWidget *parent)
    :QFrame::QFrame(parent)
{
    this->initUI();
}

void CustWidget::initUI()
{
    // 创建布局
    QFormLayout* layout = new QFormLayout(this);
    // 创建三个滑条
    QSlider* slider1 = new QSlider;
    slider1->setRange(0,255);   // 有效范围
    QSlider* slider2 = new QSlider;
    slider2->setRange(0,255);
    QSlider* slider3 = new QSlider;
    slider3->setRange(0,255);
    // 设置滑条的方向是水平方向
    slider1->setOrientation(Qt::Horizontal);
    slider2->setOrientation(Qt::Horizontal);
    slider3->setOrientation(Qt::Horizontal);
    // 把它们添加到布局中
    layout->addRow("Red:", slider1);
    layout->addRow("Green:", slider2);
    layout->addRow("Blue:", slider3);
    // 设置边框为面板
    this->setFrameShape(QFrame::Panel);
}

滑块条是 QSlider 组件,它默认的方向是垂直的,所以要将方向设定为水平。自定义组件还用到了 QFormLayout 类,它是布局类,类似 HTML Form 元素的布局方式,即表单。一般分为两列,左列是字段标题,右列是字段内容。

CustWidget 组件定义好了,接下来就是 MyWidgetAction 类,派生自 QWidgetAction。

// 头文件
#ifndef MYWIDGETACTION_H
#define MYWIDGETACTION_H

#include <QWidgetAction>
#include "custWidget.h"

class MyWidgetAction : public QWidgetAction
{
public:
    MyWidgetAction(QObject *parent);

protected:
    QWidget *createWidget(QWidget *parent) override;
};

#endif

// 代码文件
#include "myWidgetAction.h"

MyWidgetAction::MyWidgetAction(QObject *parent)
    :QWidgetAction::QWidgetAction(parent)
{
}

QWidget *MyWidgetAction::createWidget(QWidget *parent)
{
    CustWidget* w = new CustWidget(parent);
    return w;
}

整体逻辑很简单,就是返回 CustWidget 的实例。

 

然后咱们在前面 QWidgetAction 的示例上再添加一个菜单项,使用咱们刚定义的 MyWidgetAction。

MyWindow::MyWindow()
    :QMainWindow((QWidget*)nullptr)
{
    // 创建菜单栏
    QMenuBar *menubar = this->menuBar();
    // 创建菜单
    QMenu *menu = menubar->addMenu("应用程序");
    ……
    // 下面这个是自定义的
    MyWidgetAction *custAct = new MyWidgetAction(menu);
    menu->addAction(custAct);
}

最后,咱们来看看效果。

【Qt6】QWidgetAction 的使用

这效果不错吧。

好了,今天就水到这里了,有空咱们继续聊。文章来源地址https://www.toymoban.com/news/detail-630230.html

到了这里,关于【Qt6】QWidgetAction 的使用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt6.3.2下QChart的使用

    Qt小白。 看了很多关于QCharts的使用说明。一直没能在Qt6.3.2下使用成功。总结一下失败经验。 环境:win11,Qt6.3.2 选择在线安装,安装的时候选择6.3.2,下面的additional library全选 生成新工程后,如果直接用QChart是会有各种奇怪错误。需要在CMakeList.txt中增加两行: 其他代码的写

    2023年04月21日
    浏览(41)
  • 详解QT6.5在线下载—使用国内镜像

    建议去国内的镜像网站下,官网很慢 清华大学:Index of /qt/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 北京理工大学:http://mirror.bit.edu.cn/qtproject/ 中国互联网络信息中心:Index of /qt/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror 推荐使用清华大学的网站 Index of /qt

    2024年02月15日
    浏览(68)
  • 如何在qt6中使用replaceFirst和replaceLast

    在qt6中replace默认是replaceAll的,没有replaceFirst和replaceLast,但是可以运用QString提供的以下两个方法现实 不区分大小写用 Qt::CaseInsensitive 。 from默认值为0,从左往右搜索,如果from为-1,则搜索从最后一个字符开始;如果是-2,则在倒数第二个字符处,依此类推。 例如,从第4个位

    2024年02月10日
    浏览(32)
  • 使用 Qt6-mingw 编译 OpenCV 源码

    Qt 6.6.0 win11 x86_64 网址: https://cmake.org/download/ 选择对应的版本下载 将 cmake.exe 所在的 bin 目录添加到环境变量中 网址: https://opencv.org/releases/ 选择对应的版本下载 Source资源包 解压 “opencv-4.9.0.zip”, 将其解压的 “opencv-4.9.0” 存放到指定路径 “Path” (例如在我的电脑上 “Path”

    2024年01月24日
    浏览(47)
  • 使用Python和Qt6(PySide6)创建GUI应用1简介

    在本书从GUI开发的基本原理逐步过渡到使用PySide6创建您自己的、功能齐全的桌面应用程序。 图形用户界面(GUI Graphical User Interface) 历史悠久,可追溯到20世纪60年代。斯坦福大学的NLS(ON-Line 系统引入了鼠标和窗口概念,并于1968年首次公开展示。随后,施乐公司于1973年推出

    2024年02月04日
    浏览(45)
  • QT QModbusTcpClient使用ModbusTcp协议与硬件通信实战例子

        给了一个显示屏和显示屏的通信文档,用ModbusTcp协议与其通信,读取或者写入显示屏相应的内容,以满足项目需要 文档部分截图如下 屏幕如下图所示: 我需要写入改写其中的物料名称,待领料数量等,就是上位机与硬件通信 对于熟悉modbusTcp协议的,可以根据协议和通

    2024年01月25日
    浏览(37)
  • 使用java写一个对接flink的例子

    Maven依赖: 其中, flink.version 和 scala.binary.version 都需要替换为实际使用的版本号。 模拟数据生成: 这个程序使用 Flink 的 generateSequence() 方法生成 1000 个从 0 到 999 的数字作为模拟数据,将它们转化为字符串并拼接成键值对,然后使用 Flink 的 Kafka 生产者将数据写入到 Kafka 的

    2024年02月15日
    浏览(42)
  • 十五)Stable Diffusion使用教程:另一个线稿出3D例子

    案例:黄金首饰出图 1)线稿,可以进行色阶加深,不易丢失细节; 2)文生图,精确材质、光泽、工艺(抛光、拉丝等)、形状(包括深度等,比如镂空)和渲染方式(3D、素描、线稿等)提示词,负面提示词; 3)seed调-1,让ai随机出图; 4)开启controlnet,上传线稿图,选择

    2024年02月07日
    浏览(43)
  • QT6 for android 安装教程记录(版本Qt6.5.2)

    本文记录首次安装QT for andriod的详细记录。 网上的信息和资料非常多,收集和整理以及遇到的问题也各异,对新手首次接触相关开发和部署环境并不是清晰,因此,特将相关详细配置记录。 首先,开发QT for andriod 不建议使用QT5.15的版本,因为该版本不能区分相关的CPU架构,而

    2024年02月03日
    浏览(52)
  • Qt6使用QChartView类与鼠标事件实现波形的缩放、平移、坐标轴单轴缩放与鼠标悬停显示点的数据

            说在前面,本人也是近段时间刚开始学习Qt,实现上述功能的方法可能并不是最优,写此篇文章也是记录下学习的过程,也与大家分享一下。(在此先描述,后面会附上代码)(前面说的会比较基础)         首先,要使用QChartView类得现在.pro文件中加入:(得确保

    2024年02月09日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包