当前位置: 首页 > news >正文

qt-C++笔记之自定义类继承自 `QObject` 与 `QWidget` 及开发方式详解

qt-C++笔记之自定义类继承自 QObjectQWidget 及开发方式详解

在这里插入图片描述

code review!
参考笔记
1.qt-C++笔记之父类窗口、父类控件、对象树的关系
2.qt-C++笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理
3.qt-C++笔记之自定义类继承自 QObjectQWidget 及开发方式详解

在 Qt C++ 开发中,自定义类的继承选择(QObjectQWidget)以及开发方式(纯代码、使用 Qt Designer 拖拽控件、通过 “Promote to” 功能)对项目的架构和维护性具有重要影响。本指南将系统性地介绍这两种继承方式的区别,并通过具体示例展示不同开发方法的整体结构和父对象管理策略。同时,还将详细解释 Q_OBJECT 宏与 moc 处理的相关要求。

目录

  • Qt C++ 开发指南:自定义类继承自 QObjectQWidget 及开发方式详解
    • 目录
    • 1. 继承自 QObjectQWidget 的区别
      • 1.1 QObject
      • 1.2 QWidget
      • 1.3 关键区别
    • 2. 开发方式概述
      • 2.1 纯代码开发
      • 2.2 使用 Qt Designer 拖拽预设控件开发
      • 2.3 使用 Qt Designer 拖拽控件并 “Promote to” 自定义类开发
    • 3. Q_OBJECT 宏与 moc 处理
      • 3.1 Q_OBJECT 宏的要求
      • 3.2 moc 文件的包含方式
        • 3.2.1 类声明在独立头文件中
        • 3.2.2 类定义在源文件中
      • 3.3 类声明单独为一个文件与 #include "moc_Class.cpp" 的关系
    • 4. 父对象传递策略
      • 4.1 纯代码开发中的父对象传递
        • 4.1.1 继承自 QObject 的类
        • 4.1.2 继承自 QWidget 的类
      • 4.2 使用 Qt Designer 开发中的父对象传递
        • 4.2.1 继承自 QObject 的类
        • 4.2.2 继承自 QWidget 的类
      • 4.3 Promote to 功能的继承要求
    • 5. 具体示例
      • 5.1 纯代码开发中继承自 QObject 的类
      • 5.2 纯代码开发中继承自 QWidget 的类
      • 5.3 使用 Qt Designer 拖拽控件并 “Promote to” 自定义 QWidget
        • 5.3.1 Promote to 功能的继承要求
    • 6. 最佳实践建议
    • 7. 总结
    • 8. 参考资料

1. 继承自 QObjectQWidget 的区别

1.1 QObject

  • 基本特性
    • QObject 是 Qt 对象模型的基类,提供了对象树、信号与槽机制、属性系统等核心功能。
    • 不具备任何用户界面(UI)元素,无法直接用于显示内容。
  • 适用场景
    • 适用于非 UI 类,如数据模型、控制器、管理器等。
    • 用于需要利用 Qt 的信号与槽机制、事件系统等特性的类。
  • 关键点
    • 无可视化:不适用于直接展示界面。
    • 逻辑与数据处理:主要用于处理应用程序的逻辑和数据。

1.2 QWidget

  • 基本特性
    • QWidget 继承自 QObject,是所有可视化组件的基类。
    • 提供了绘制、事件处理、布局管理等与 UI 相关的功能。
  • 适用场景
    • 用于构建自定义的用户界面组件,如自定义按钮、窗口、对话框等。
    • 任何需要在界面上显示的组件都应该继承自 QWidget 或其子类。
  • 关键点
    • 可视化:具备显示界面的能力。
    • 用户交互:支持用户的各种交互操作。

1.3 关键区别

特性QObjectQWidget
可视化
用途逻辑处理、数据管理构建和展示用户界面
基类QObject
适用场景非 UI 类,如控制器、数据模型UI 组件,如按钮、窗口、对话框

2. 开发方式概述

Qt 提供了多种开发方式,以满足不同项目的需求和开发者的偏好。本节将介绍三种主要的开发方式,并通过示例展示其整体结构。

2.1 纯代码开发

特点

  • 所有 UI 和逻辑通过代码手动编写。
  • 提供了最大的灵活性和控制力。

优点

  • 适用于动态生成 UI 或需要高度定制化的场景。
  • 便于版本控制,因为所有内容都在代码中。

缺点

  • 编写和维护大量 UI 代码较为繁琐。
  • 缺乏直观的可视化设计。

2.2 使用 Qt Designer 拖拽预设控件开发

特点

  • 使用 Qt Designer 的可视化界面,通过拖拽预设的控件(如按钮、标签等)来设计 UI。
  • 生成 .ui 文件,Qt 的 uic 工具将其转换为代码。

优点

  • 直观、快速地设计和布局 UI。
  • 易于调整和预览 UI 效果。

缺点

  • 预设控件的功能有限,复杂定制化需要额外代码。
  • 需要在代码中与生成的 UI 进行集成。

2.3 使用 Qt Designer 拖拽控件并 “Promote to” 自定义类开发

特点

  • 在 Qt Designer 中拖拽预设控件(通常是占位符,如 QWidget),然后通过 “Promote to” 功能将其替换为自定义的子类。
  • 允许在可视化设计中集成自定义的 UI 组件。

优点

  • 结合了可视化设计的便捷性和自定义组件的灵活性。
  • 使得复杂或定制化的 UI 组件能够在 Designer 中被复用和管理。

缺点

  • 需要确保自定义类正确继承自 QWidget 或其子类。
  • 需要维护自定义类与 Designer 生成的 UI 之间的同步。

3. Q_OBJECT 宏与 moc 处理

3.1 Q_OBJECT 宏的要求

  • 用途
    • 启用 Qt 的元对象特性,如信号与槽、动态属性、反射等。
  • 要求
    • 类必须继承自 QObject 或其子类(如 QWidget)。
    • 类声明中必须包含 Q_OBJECT 宏。
    • 必须经过 Qt 的 moc(Meta-Object Compiler)处理,以生成必要的元对象代码。

3.2 moc 文件的包含方式

3.2.1 类声明在独立头文件中

如果类声明在独立的头文件(如 MyClass.h)中,moc 会自动生成 moc_MyClass.cpp,并由编译系统处理。无需在源文件中手动包含 moc 文件。

示例

MyClass.h

#ifndef MYCLASS_H
#define MYCLASS_H#include <QObject>class MyClass : public QObject {Q_OBJECTpublic:explicit MyClass(QObject *parent = nullptr);signals:void mySignal();
};#endif // MYCLASS_H

MyClass.cpp

#include "MyClass.h"MyClass::MyClass(QObject *parent) : QObject(parent) {}

main.cpp

#include <QCoreApplication>
#include "MyClass.h"int main(int argc, char *argv[]) {QCoreApplication app(argc, argv);MyClass obj;// 使用 obj...return app.exec();
}

说明

  • moc 会自动处理 MyClass.h,无需手动包含 moc 文件。
  • 确保项目文件(如 .proCMakeLists.txt)正确配置,以包含 moc 处理。
3.2.2 类定义在源文件中

如果类声明和实现都在同一个源文件中(如 main.cpp),需要在文件末尾手动包含 moc 文件,以确保 moc 处理该类。

示例

main.cpp

#include <QApplication>
#include <QPushButton>
#include <QObject>
#include <QMessageBox>class MyWidget : public QPushButton {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr) : QPushButton("Click Me", parent) {connect(this, &QPushButton::clicked, this, &MyWidget::onClicked);}private slots:void onClicked() {QMessageBox::information(this, "Message", "Button clicked!");}
};int main(int argc, char *argv[]) {QApplication app(argc, argv);MyWidget w;w.show();return app.exec();
}#include "main.moc"

说明

  • 由于 MyWidget 类在 main.cpp 中定义,需在文件末尾添加 #include "main.moc",以确保 moc 处理该类。
  • moc 生成的 main.moc 文件会被编译并链接。

3.3 类声明单独为一个文件与 #include "moc_Class.cpp" 的关系

  • 推荐做法:将类声明放在独立的头文件中,让 moc 自动处理。避免手动包含 moc 文件,这简化了构建过程,并减少了错误的可能性。

  • 不推荐做法:手动包含 moc 文件(如 #include "moc_MyClass.cpp"),这可能导致重复定义或链接错误,除非有特定需求(如某些编译器或构建系统的限制)。

4. 父对象传递策略

在 Qt 中,父对象的传递对于内存管理和对象层级关系至关重要。本节将详细介绍在不同继承类和开发方式下,如何合理传递父对象。

4.1 纯代码开发中的父对象传递

在纯代码开发中,开发者需要手动创建和管理对象及其父子关系。传递 this 或父类窗口主要取决于对象的用途和生命周期管理需求。

4.1.1 继承自 QObject 的类

示例:在主窗口中创建一个 MyObject 实例,并将主窗口作为父对象传递。

MyObject.h

#ifndef MYOBJECT_H
#define MYOBJECT_H#include <QObject>class MyObject : public QObject {Q_OBJECTpublic:explicit MyObject(QObject *parent = nullptr);signals:void mySignal(const QString &message);public slots:void mySlot(const QString &message);
};#endif // MYOBJECT_H

MyObject.cpp

#include "MyObject.h"
#include <QDebug>MyObject::MyObject(QObject *parent) : QObject(parent) {}void MyObject::mySlot(const QString &message) {qDebug() << "Received message:" << message;
}

main.cpp

#include <QApplication>
#include <QMainWindow>
#include "MyObject.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QMainWindow mainWindow;mainWindow.show();// 创建 MyObject,并将 mainWindow 作为父对象MyObject obj(&mainWindow);// 连接信号与槽QObject::connect(&obj, &MyObject::mySignal, &obj, &MyObject::mySlot);// 触发信号emit obj.mySignal("Hello from MyObject!");return app.exec();
}

解释

  • 传递 &mainWindow 作为父对象:确保 MyObject 的生命周期与 mainWindow 一致。当 mainWindow 销毁时,obj 也会被自动销毁。
  • 内存管理:无需手动管理 MyObject 的内存,Qt 的对象树机制自动处理。
4.1.2 继承自 QWidget 的类

示例:在主窗口中创建一个自定义的 MyWidget,并将主窗口作为父对象传递。

MyWidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>class QPushButton;class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr);private slots:void handleButton();private:QPushButton *button;
};#endif // MYWIDGET_H

MyWidget.cpp

#include "MyWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {button = new QPushButton("Click Me", this);QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(button);setLayout(layout);connect(button, &QPushButton::clicked, this, &MyWidget::handleButton);
}void MyWidget::handleButton() {QMessageBox::information(this, "Message", "Button clicked!");
}

main.cpp

#include <QApplication>
#include <QMainWindow>
#include "MyWidget.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QMainWindow mainWindow;MyWidget *widget = new MyWidget(&mainWindow); // 传递 mainWindow 作为父对象mainWindow.setCentralWidget(widget);mainWindow.show();return app.exec();
}

解释

  • 传递 &mainWindow 作为父对象:使 MyWidget 成为 mainWindow 的中央控件,自动管理其生命周期和显示。
  • 层级关系MyWidget 在主窗口中显示,并且会随着主窗口一起显示和隐藏。

4.2 使用 Qt Designer 开发中的父对象传递

使用 Qt Designer 时,父对象的传递和管理主要由 Designer 生成的 UI 代码处理,开发者无需手动传递 this。以下分为两种情况:

4.2.1 继承自 QObject 的类

由于 QObject 本身不具备可视化能力,通常在 Qt Designer 中不直接使用继承自 QObject 的类。但可以在代码中作为逻辑组件创建和管理。

示例:在使用 Qt Designer 设计的主窗口中创建一个 MyObject 实例。

MyMainWindow.h

#ifndef MYMAINWINDOW_H
#define MYMAINWINDOW_H#include <QMainWindow>
#include "MyObject.h"QT_BEGIN_NAMESPACE
namespace Ui { class MyMainWindow; }
QT_END_NAMESPACEclass MyMainWindow : public QMainWindow {Q_OBJECTpublic:explicit MyMainWindow(QWidget *parent = nullptr);~MyMainWindow();private:Ui::MyMainWindow *ui;MyObject *obj;
};#endif // MYMAINWINDOW_H

MyMainWindow.cpp

#include "MyMainWindow.h"
#include "ui_MyMainWindow.h"MyMainWindow::MyMainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MyMainWindow), obj(new MyObject(this)) // 传递 this 作为父对象
{ui->setupUi(this);// 连接信号与槽connect(obj, &MyObject::mySignal, obj, &MyObject::mySlot);// 触发信号emit obj->mySignal("Hello from MyObject in Designer!");
}MyMainWindow::~MyMainWindow() {delete ui;
}

解释

  • MyObject 作为 MyMainWindow 的子对象:通过传递 thisMyObject 的生命周期由 MyMainWindow 管理。
  • UI 设计与逻辑分离MyObject 不直接参与 UI 设计,而是作为逻辑组件存在。
4.2.2 继承自 QWidget 的类

在使用 Qt Designer 拖拽控件并进行 “Promote to” 操作时,自定义的 QWidget 子类会自动被设置为相应控件的子对象,父对象由 Designer 生成的 UI 代码管理。

示例:在 Qt Designer 中将一个 QPushButton 提升为自定义的 MyButton

  1. 创建自定义 MyButton

MyButton.h

#ifndef MYBUTTON_H
#define MYBUTTON_H#include <QPushButton>class MyButton : public QPushButton {Q_OBJECTpublic:explicit MyButton(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent *event) override;private slots:void onClicked();
};#endif // MYBUTTON_H

MyButton.cpp

#include "MyButton.h"
#include <QPainter>
#include <QMessageBox>MyButton::MyButton(QWidget *parent) : QPushButton(parent) {setText("Custom Button");connect(this, &QPushButton::clicked, this, &MyButton::onClicked);
}void MyButton::paintEvent(QPaintEvent *event) {QPainter painter(this);painter.setBrush(Qt::cyan);painter.drawRect(rect());QPushButton::paintEvent(event);
}void MyButton::onClicked() {QMessageBox::information(this, "Custom Button", "MyButton clicked!");
}
  1. 在 Qt Designer 中进行 “Promote to” 操作

    • 打开 mywidget.ui
    • 拖拽一个 QPushButton 到设计区域,设置对象名称为 myButton
    • 右键点击该按钮,选择 “Promote to…”。
    • 在弹出的对话框中填写:
      • Promoted class name: MyButton
      • Header file: MyButton.h
    • 点击 “Add”,然后 “Promote”。
  2. 更新自定义类

MyWidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>namespace Ui {
class MyWidget;
}class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr);~MyWidget();private:Ui::MyWidget *ui;
};#endif // MYWIDGET_H

MyWidget.cpp

#include "MyWidget.h"
#include "ui_mywidget.h"
#include "MyButton.h" // 确保包含自定义类的头文件MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
{ui->setupUi(this);// 无需手动传递父对象,Designer 自动处理
}MyWidget::~MyWidget() {delete ui;
}

main.cpp

#include <QApplication>
#include "MyWidget.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);MyWidget w;w.show();return app.exec();
}

解释

  • 父对象自动管理:通过 “Promote to” 功能,MyButton 实例的父对象由 Designer 生成的 UI 代码自动设置为包含它的父控件(如 MyWidget)。
  • 无需手动传递 this:在 UI 设计中,父子关系由布局和 UI 文件决定,开发者无需手动传递 this

4.3 Promote to 功能的继承要求

在使用 Qt Designer 的 “Promote to” 功能时,确保自定义类能够正确替换预设控件,需要满足以下继承要求:

  • 继承自相同或兼容的基类

    • 自定义类必须继承自被替换的控件的基类。
    • 具体来说:
      • 替换 QPushButton:自定义类应继承自 QPushButton
      • 替换 QLabel:自定义类应继承自 QLabel
      • 替换 QWidget:自定义类应继承自 QWidget
    • 示例
      • 如果在 Qt Designer 中将 QPushButton 提升为 MyButton,则 MyButton 应继承自 QPushButton 而不是直接继承自 QWidget。这样可以确保接口兼容性和行为一致性,避免运行时错误。
  • 构造函数匹配

    • 自定义类应实现与基类兼容的构造函数,通常接受一个 QWidget 指针作为父对象。
    • 例如:
      explicit MyButton(QWidget *parent = nullptr);
      
    • 这确保在 Designer 生成的代码中,自定义控件能够正确初始化。
  • 正确的头文件包含

    • 在 “Promote to” 对话框中,需提供自定义类的头文件路径(如 MyButton.h),确保编译器能够找到并包含自定义类的定义。
    • 这对于项目的构建系统(如 qmake 或 CMake)来说至关重要,必须确保头文件路径正确配置。
  • 使用 Q_OBJECT 宏(如果需要)

    • 如果自定义类需要使用信号与槽机制,必须在类声明中包含 Q_OBJECT 宏,并确保 moc 正确处理。
    • 例如:
      class MyButton : public QPushButton {Q_OBJECT// ...
      };
      
  • 确保自定义类已在项目中编译

    • 自定义类必须包含在项目的编译过程中,确保 moc 处理和编译生成的元对象代码。
    • 在使用 CMake 时,通常需要将自定义类的头文件添加到 CMakeLists.txt 中的 HEADERSSOURCES 部分。
  • 处理虚函数和事件

    • 如果自定义类重写了基类的虚函数(如 paintEvent),需确保调用基类的实现,以保持控件的基本功能。
    • 例如:
      void MyButton::paintEvent(QPaintEvent *event) {// 自定义绘制QPushButton::paintEvent(event);
      }
      
  • 信号与槽的兼容性

    • 自定义类中定义的信号与槽应与基类的信号与槽兼容,确保在 Designer 中能够正确连接和使用。
    • 避免更改基类信号与槽的签名,除非有充分的理由和处理逻辑。

总结
使用 “Promote to” 功能时,遵循以上继承要求能够确保自定义控件在 Qt Designer 中正确工作,并与生成的 UI 代码无缝集成。这不仅提高了开发效率,还保持了代码的可维护性和扩展性。特别是 继承自被替换控件的基类 是确保功能和行为一致性的关键步骤,避免因继承不当导致的兼容性问题。

5. 具体示例

通过以下具体示例,进一步展示不同继承方式和开发方法下的整体结构。

5.1 纯代码开发中继承自 QObject 的类

MyObject.h

#ifndef MYOBJECT_H
#define MYOBJECT_H#include <QObject>class MyObject : public QObject {Q_OBJECTpublic:explicit MyObject(QObject *parent = nullptr);signals:void mySignal(const QString &message);public slots:void mySlot(const QString &message);
};#endif // MYOBJECT_H

MyObject.cpp

#include "MyObject.h"
#include <QDebug>MyObject::MyObject(QObject *parent) : QObject(parent) {}void MyObject::mySlot(const QString &message) {qDebug() << "Received message:" << message;
}

main.cpp

#include <QApplication>
#include <QMainWindow>
#include "MyObject.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QMainWindow mainWindow;mainWindow.show();MyObject obj(&mainWindow); // 传递 mainWindow 作为父对象QObject::connect(&obj, &MyObject::mySignal, &obj, &MyObject::mySlot);emit obj.mySignal("Hello from MyObject!");return app.exec();
}

5.2 纯代码开发中继承自 QWidget 的类

MyWidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget>class QPushButton;class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr);private slots:void handleButton();private:QPushButton *button;
};#endif // MYWIDGET_H

MyWidget.cpp

#include "MyWidget.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>MyWidget::MyWidget(QWidget *parent) : QWidget(parent) {button = new QPushButton("Click Me", this);QVBoxLayout *layout = new QVBoxLayout(this);layout->addWidget(button);setLayout(layout);connect(button, &QPushButton::clicked, this, &MyWidget::handleButton);
}void MyWidget::handleButton() {QMessageBox::information(this, "Message", "Button clicked!");
}

main.cpp

#include <QApplication>
#include <QMainWindow>
#include "MyWidget.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);QMainWindow mainWindow;MyWidget *widget = new MyWidget(&mainWindow); // 传递 mainWindow 作为父对象mainWindow.setCentralWidget(widget);mainWindow.show();return app.exec();
}

5.3 使用 Qt Designer 拖拽控件并 “Promote to” 自定义 QWidget

步骤

  1. 创建自定义 MyButton

    MyButton.h

    #ifndef MYBUTTON_H
    #define MYBUTTON_H#include <QPushButton>class MyButton : public QPushButton {Q_OBJECTpublic:explicit MyButton(QWidget *parent = nullptr);protected:void paintEvent(QPaintEvent *event) override;private slots:void onClicked();
    };#endif // MYBUTTON_H
    

    MyButton.cpp

    #include "MyButton.h"
    #include <QPainter>
    #include <QMessageBox>MyButton::MyButton(QWidget *parent) : QPushButton(parent) {setText("Custom Button");connect(this, &QPushButton::clicked, this, &MyButton::onClicked);
    }void MyButton::paintEvent(QPaintEvent *event) {QPainter painter(this);painter.setBrush(Qt::cyan);painter.drawRect(rect());QPushButton::paintEvent(event);
    }void MyButton::onClicked() {QMessageBox::information(this, "Custom Button", "MyButton clicked!");
    }
    
  2. 在 Qt Designer 中进行 “Promote to” 操作

    • 打开 mywidget.ui
    • 拖拽一个 QPushButton 到设计区域,设置对象名称为 myButton
    • 右键点击该按钮,选择 “Promote to…”。
    • 在弹出的对话框中填写:
      • Promoted class name: MyButton
      • Header file: MyButton.h
    • 点击 “Add”,然后 “Promote”。
  3. 更新自定义类

    MyWidget.h

    #ifndef MYWIDGET_H
    #define MYWIDGET_H#include <QWidget>namespace Ui {
    class MyWidget;
    }class MyWidget : public QWidget {Q_OBJECTpublic:explicit MyWidget(QWidget *parent = nullptr);~MyWidget();private:Ui::MyWidget *ui;
    };#endif // MYWIDGET_H
    

    MyWidget.cpp

    #include "MyWidget.h"
    #include "ui_mywidget.h"
    #include "MyButton.h" // 确保包含自定义类的头文件MyWidget::MyWidget(QWidget *parent): QWidget(parent), ui(new Ui::MyWidget)
    {ui->setupUi(this);// 无需手动传递父对象,Designer 自动处理
    }MyWidget::~MyWidget() {delete ui;
    }
    

    main.cpp

    #include <QApplication>
    #include "MyWidget.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);MyWidget w;w.show();return app.exec();
    }
    

关键点

  • 自定义控件的父对象由 Qt Designer 自动设置:确保代码简洁且维护简单。
  • 自定义控件必须继承自 QWidget 或其子类:以确保在 UI 中正确显示和交互。
5.3.1 Promote to 功能的继承要求

在使用 Qt Designer 的 “Promote to” 功能时,确保自定义类能够正确替换预设控件,需要满足以下继承要求:

  • 继承自相同或兼容的基类

    • 自定义类必须继承自被替换的控件的基类。
    • 具体来说:
      • 替换 QPushButton:自定义类应继承自 QPushButton
      • 替换 QLabel:自定义类应继承自 QLabel
      • 替换 QWidget:自定义类应继承自 QWidget
    • 示例
      • 如果在 Qt Designer 中将 QPushButton 提升为 MyButton,则 MyButton 应继承自 QPushButton 而不是直接继承自 QWidget。这样可以确保接口兼容性和行为一致性,避免运行时错误。
  • 构造函数匹配

    • 自定义类应实现与基类兼容的构造函数,通常接受一个 QWidget 指针作为父对象。
    • 例如:
      explicit MyButton(QWidget *parent = nullptr);
      
    • 这确保在 Designer 生成的代码中,自定义控件能够正确初始化。
  • 正确的头文件包含

    • 在 “Promote to” 对话框中,需提供自定义类的头文件路径(如 MyButton.h),确保编译器能够找到并包含自定义类的定义。
    • 这对于项目的构建系统(如 qmake 或 CMake)来说至关重要,必须确保头文件路径正确配置。
  • 使用 Q_OBJECT 宏(如果需要)

    • 如果自定义类需要使用信号与槽机制,必须在类声明中包含 Q_OBJECT 宏,并确保 moc 正确处理。
    • 例如:
      class MyButton : public QPushButton {Q_OBJECT// ...
      };
      
  • 确保自定义类已在项目中编译

    • 自定义类必须包含在项目的编译过程中,确保 moc 处理和编译生成的元对象代码。
    • 在使用 CMake 时,通常需要将自定义类的头文件添加到 CMakeLists.txt 中的 HEADERSSOURCES 部分。
  • 处理虚函数和事件

    • 如果自定义类重写了基类的虚函数(如 paintEvent),需确保调用基类的实现,以保持控件的基本功能。
    • 例如:
      void MyButton::paintEvent(QPaintEvent *event) {// 自定义绘制QPushButton::paintEvent(event);
      }
      
  • 信号与槽的兼容性

    • 自定义类中定义的信号与槽应与基类的信号与槽兼容,确保在 Designer 中能够正确连接和使用。
    • 避免更改基类信号与槽的签名,除非有充分的理由和处理逻辑。

总结
使用 “Promote to” 功能时,遵循以上继承要求能够确保自定义控件在 Qt Designer 中正确工作,并与生成的 UI 代码无缝集成。这不仅提高了开发效率,还保持了代码的可维护性和扩展性。特别是 继承自被替换控件的基类 是确保功能和行为一致性的关键步骤,避免因继承不当导致的兼容性问题。

6. 最佳实践建议

  • 合理管理父对象

    • 确保对象的父子关系符合逻辑需求,避免内存泄漏或对象过早销毁。
    • 使用 Qt 的对象树机制自动管理内存,减少手动内存管理的负担。
  • 使用 Qt Designer 时依赖自动管理

    • 利用 Qt Designer 的布局和父对象管理功能,减少手动传递 this 的需要。
    • 通过 “Promote to” 功能集成自定义控件,保持代码与 UI 设计的同步。
  • 独立逻辑类使用 QObject 继承

    • 将不涉及 UI 的逻辑类继承自 QObject,并合理设置父对象以管理生命周期。
    • 保持 UI 设计与业务逻辑的分离,提高代码的可维护性和复用性。
  • 自定义 UI 组件继承自 QWidget 或其子类

    • 确保所有自定义的 UI 组件继承自 QWidget,以利用 Qt 的可视化和事件处理能力。
    • 在需要时通过 “Promote to” 功能集成到 Designer 中,提升开发效率。
  • 正确使用 Q_OBJECT

    • 对于需要信号与槽机制的类,确保在类声明中包含 Q_OBJECT 宏。
    • 将类声明放在独立的头文件中,让 moc 自动处理,避免手动包含 moc 文件带来的复杂性。
  • 保持代码和 UI 的同步

    • 在使用 Qt Designer 设计 UI 时,及时更新自定义类和代码,确保 UI 与逻辑的一致性。
    • 定期进行全量重新构建(Clean Build),避免 moc 处理遗漏的问题。

7. 总结

在 Qt C++ 开发中,选择继承自 QObjectQWidget 取决于类的功能需求。QObject 适用于非 UI 类,提供信号与槽等核心功能;而 QWidget 则用于构建和展示用户界面组件,具备可视化和事件处理能力。开发方式方面,纯代码开发提供了最大的灵活性,适用于高度定制化的场景;使用 Qt Designer 拖拽控件则提高了 UI 设计的效率;通过 “Promote to” 功能,可以结合可视化设计与自定义组件,实现更复杂和定制化的界面设计。

此外,正确管理父对象的传递和理解 Q_OBJECT 宏与 moc 的处理机制,对于确保应用程序的稳定性和可维护性至关重要。通过遵循本指南中的最佳实践,开发者可以更高效地在 Qt C++ 中开发自定义类,构建结构良好、功能强大的应用程序。

8. 参考资料

  • Qt 官方文档
  • Qt Designer 使用指南
  • Qt 信号与槽机制

相关文章:

qt-C++笔记之自定义类继承自 `QObject` 与 `QWidget` 及开发方式详解

qt-C笔记之自定义类继承自 QObject 与 QWidget 及开发方式详解 code review! 参考笔记 1.qt-C笔记之父类窗口、父类控件、对象树的关系 2.qt-C笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理 3.qt-C笔记之自定义类继承自 QObject 与 QW…...

利用git上传项目到GitHub

GitHub是基于git实现的代码托管。git是目前最好用的版本控制系统了&#xff0c;非常受欢迎&#xff0c;比之svn更好。 GitHub可以免费使用&#xff0c;并且快速稳定。 利用GitHub&#xff0c;你可以将项目存档&#xff0c;与其他人分享交流&#xff0c;并让其他开发者帮助你一…...

机器学习预处理-表格数据的空值处理

机器学习预处理-表格数据的空值处理 机器学习预处理-表格数据的分析与可视化中详细介绍了表格数据的python可视化&#xff0c;可视化能够帮助我们了解数据的构成和分布&#xff0c;是我们进行机器学习的必备步骤。上文中也提及&#xff0c;原始的数据存在部分的缺失&#xff0…...

python学opencv|读取图像(十二)BGR图像转HSV图像

【1】引言 前述已经学习了opencv中图像BGR相关知识&#xff0c;文章链接包括且不限于下述&#xff1a; python学opencv|读取图像&#xff08;六&#xff09;读取图像像素RGB值_opencv读取灰度图-CSDN博客 python学opencv|读取图像&#xff08;七&#xff09;抓取像素数据顺利…...

【C语言】库函数常见的陷阱与缺陷(六):输入输出函数

目录 一、printf 函数 1.1. 功能与用法 1.2. 陷阱与缺陷 1.3. 安全使用建议 1.4. 代码示例 二、scanf 函数 2.1. 功能与用法 2.2. 陷阱与缺陷 2.3. 安全使用建议 2.4. 代码示例 三、gets 函数 3.1. 功能与用法 3.2. 陷阱与缺陷 3.3. 安全使用建议 3.4. 代码示例…...

sunset: midnight

https://www.vulnhub.com/entry/sunset-midnight,517/ 主机发现端口扫描 探测存活主机&#xff0c;8是靶机 nmap -sP 192.168.56.0/24 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-12-05 16:49 CST Nmap scan report for 192.168.56.1 …...

CSS Backgrounds(背景)

CSS Backgrounds(背景) Introduction(介绍) CSS backgrounds play a crucial role in web design, allowing developers to apply colors, images, and other decorative elements to the background of HTML elements. This enhances the visual appeal of web pages and he…...

D101【python 接口自动化学习】- pytest进阶之fixture用法

day101 pytest的fixture执行顺序 学习日期&#xff1a;20241218 学习目标&#xff1a;pytest基础用法 -- pytest的fixture执行顺序 学习笔记&#xff1a; fixtrue的作用范围 实战结果 import pytestpytest.fixture(scopesession) def test_session():print(我是 session f…...

HCIA-Access V2.5_4_1_1路由协议基础_IP路由表

大型网络的拓扑结构一般会比较复杂&#xff0c;不同的部门&#xff0c;或者总部和分支可能处在不同的网络中&#xff0c;此时就需要使用路由器来连接不同的网络&#xff0c;实现网络之间的数据转发。 本章将介绍路由协议的基础知识、路由表的分类、静态路由基础与配置、VLAN间…...

Meta重磅发布Llama 3.3 70B:开源AI模型的新里程碑

在人工智能领域&#xff0c;Meta的最新动作再次引起了全球的关注。今天&#xff0c;我们见证了Meta发布的Llama 3.3 70B模型&#xff0c;这是一个开源的人工智能模型&#xff0c;它不仅令人印象深刻&#xff0c;而且在性能上达到了一个新的高度。 一&#xff0c;技术突破&#…...

20241218_segmentation

参考&#xff1a; 使用SA模型 https://ai.meta.com/research/publications/segment-anything/讲解生物学意义 https://www.nature.com/articles/s41593-024-01714-3#Sec13 x.0 workflow 图像分割方法识别出重要的ROI区域计算ROI区域个数&#xff08;需要计算机算法&#xff…...

公链常用的共识算法

1. 工作量证明&#xff08;Proof of Work, PoW&#xff09; 工作原理&#xff1a;要求节点&#xff08;矿工&#xff09;解决一个数学难题&#xff0c;这个过程称为挖矿。第一个解决难题的矿工将有权添加一个新的区块到区块链上&#xff0c;并获得一定数量的加密货币作为奖励。…...

监控易在汽车制造行业信息化运维中的应用案例

引言 随着汽车制造行业的数字化转型不断深入&#xff0c;信息化类IT软硬件设备的运行状态监控、故障告警、报表报告以及网络运行状态监控等成为了企业运维管理的关键环节。监控易作为一款全面、高效的信息化运维管理工具&#xff0c;在汽车制造行业中发挥着重要作用。本文将结合…...

Spring Boot项目使用虚拟线程

Spring Boot项目启用虚拟线程 开始基本使用先写一个测试方法通过springboot配置项开启虚拟线程 目前存在的问题 开始 虚拟线程正式发布是在JDK21&#xff0c;对于Spring Boot版本选择3以上。 基本使用 关于虚拟线程本身的使用&#xff0c;之前已经介绍过。这里要说的是直接将…...

Deveco Studio首次编译项目初始化失败

编译项目失败 Ohpm install失败的时候重新使用管理者打开程序 build init 初始化失败遇到了以下报错信息 Installing pnpm8.13.1... npm ERR! code CERT_HAS_EXPIRED npm ERR! errno CERT_HAS_EXPIRED npm ERR! request to https://registry.npm.taobao.org/pnpm failed, r…...

Unity 开发Apple Vision Pro空间锚点应用Spatial Anchor

空间锚点具有多方面的作用 虚拟物体定位与固定&#xff1a; 位置保持&#xff1a;可以把虚拟物体固定在现实世界中的特定区域或位置。即使使用者退出程序后再次打开&#xff0c;之前锚定过的虚拟物体仍然能够出现在之前所锚定的位置&#xff0c;为用户提供连贯的体验。比如在一…...

由学习率跟batch size 关系 引起的海塞矩阵和梯度计算在训练过程中的应用思考

最近看到了个一个学习率跟batch size 关系的帖子&#xff0c;里面说 OpenAI的《An Empirical *** Training》 通过损失函数的二阶近似分析SGD的最优学习率&#xff0c;得出“学习率随着Batch Size的增加而单调递增但有上界”的结论。推导过程中将学习率作为待优化参数纳入损失函…...

PHP开发日志 ━━ 基础知识:四种不同的变量返回方式该如何调用

最近在给框架升级&#xff0c;其中涉及到古早的缓存系统升级&#xff0c;现在准备区分类型为混合、变量和普通文件&#xff0c;那么变量用什么形式存储到缓存才能给后续开发带来便利和通用性呢&#xff1f;于是就涉及到了本文的php基础知识。 好吧&#xff0c;又是一个无用的知…...

centos上配置yum源

1. 进入yum源repo的目录 cd /etc/yum.repos.d/然后可以通过ls查看下面所有的后缀为.repo的文件 2. 新建一个备份目录&#xff0c;将原有的.repo文件放到其中 mkdir yum.repos.d.backup mv *.repo yum.repos.d.backup/3. 获取阿里提供的repo配置文件 这里使用到了wget命令&a…...

Web3 时代:技术变革与未来展望

Web3作为下一代互联网技术&#xff0c;正在逐步改变我们使用互联网的方式。它的核心特点是去中心化&#xff0c;利用区块链技术&#xff0c;使得数据不再集中存储&#xff0c;用户能更好地掌控自己的信息。本文将简要介绍Web3的核心技术及其未来展望。 Web3代表的是去中心化的互…...

小程序快速实现大模型聊天机器人

需求分析&#xff1a; 基于大模型&#xff0c;打造一个聊天机器人&#xff1b;使用开放API快速搭建&#xff0c;例如&#xff1a;讯飞星火&#xff1b;先实现UI展示&#xff0c;在接入API。 最终实现效果如下&#xff1a; 一.聊天机器人UI部分 1. 创建微信小程序&#xff0c…...

用shell脚本来判断web服务是否运行(端口和进程两种方式)

判断web服务是否运行&#xff08;1、查看进程的方式判断该程序是否运行&#xff0c;2、通过查看端口的方式判断该程序是否运行&#xff09;&#xff0c;如果没有运行&#xff0c;则启动该服务并配置防火墙规则。------这里以nginx为例 一、用进程方式判断 &#xff08;1&#…...

Android运行低版本项目可能遇到的问题

Android运行低版本项目可能遇到的问题 低版本项目总是遇到各种问题的&#xff0c;耐心点 一、gradle-xxx.xxx.xxx.zip一直下载不下来 在gradle-wrapper.properties可以试下 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists zipStoreBaseGRADLE_USER_HOME …...

SQL Server 解决游标性能问题的替代方案

在 SQL Server 中&#xff0c;游标&#xff08;Cursor&#xff09;是一种用于逐行处理数据集的强大工具&#xff0c;但在某些情况下&#xff0c;它们可能会导致性能问题&#xff0c;尤其是在处理大量数据时。为了提高性能和可维护性&#xff0c;可以考虑使用其他替代方案。以下…...

【论文笔记】CLIP-guided Prototype Modulating for Few-shot Action Recognition

&#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平。 基本信息 标题: CLIP-guided Prototype Mo…...

PHP:从入门到进阶的全方位探索

PHP&#xff08;Hypertext Preprocessor&#xff09;作为一种开源的服务器端脚本语言&#xff0c;自1995年问世以来&#xff0c;凭借其简单易学、高效灵活的特点&#xff0c;迅速成为了Web开发领域的中流砥柱。无论是构建动态网页、开发Web应用程序&#xff0c;还是处理复杂的服…...

vue复习

1.试述前端开发技术发展变化历程&#xff0c;理解推动技术发展动力以及对软件开发职业的启发。 2.当前前端开发技术主要特征有哪些&#xff1f; 前后端分离开发&#xff1a; 前端专注页面展示效果与用户使用体验,后端专注为前端提供数据和服务。 工程化特征&#xff1a;模块…...

伊克罗德与九科信息共同发布RPA+AI智能机器人解决方案

12月12日&#xff0c;伊克罗德信息在上海举办“创见AI&#xff0c;迈进智能化未来——科技赋能零售电商”活动&#xff0c;与九科信息、亚马逊云科技共同探讨与分享&#xff0c;融合生成式AI技术和智能自动化&#xff08;RPA,Robotic Process Automation&#xff09;在电商零售…...

nano编辑器的使用

nano 是一个非常简单易用的命令行文本编辑器&#xff0c;它常用于在 Linux 或类 Unix 系统中快速编辑文件&#xff0c;特别适用于需要修改配置文件或快速编辑文本的场景。以下是一些常见的 nano 使用技巧和基本操作。 1. 打开文件 要使用 nano 编辑文件&#xff0c;打开终端并…...

灵当crm pdf.php存在任意文件读取漏洞

免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

Liinux下VMware Workstation Pro的安装,建议安装最新版本17.61

建议安装最新版本17.61&#xff0c;否则可能有兼容性问题 下载VMware Workstation安装软件 从官网网站下载 https://support.broadcom.com/group/ecx/productdownloads?subfamilyVMwareWorkstationPro 选择所需版本 现在最新版本是17.61&#xff0c;否则可能有兼容性问题…...

性能测试度量指标学习笔记

目录 一、概要 二、不同系统软件性能测试度量指标 三、性能测试度量指标 1、响应时间 2、用户数 3、系统处理能力 4、错误率 5、成功率 6、资源占用率 7、CPU利用率 8、内存页交换速率 9、内存占用率 10、磁盘IO 11、磁盘吞吐量 12、网络吞吐量 13、系统稳定性…...

一款可以替代Navicat的数据库管理工具

Navicat是一款非常受欢迎的数据库管理工具&#xff0c;基本支持市面上的所有数据库、而且支持跨平台。 可以说Navicat是一款功能强大、非常全面的数据库管理工具&#xff0c;提供了多种版本和定价方案&#xff0c;以满足不同用户的需求和预算。 也是很多企业的首选工具&#…...

使用C#在目录层次结构中搜索文件以查找目标字符串

例程以递归方式搜索目录层次结构中的文件以查找目标字符串。它可以搜索几乎任何类型的文件&#xff0c;即使它不包含 Windows 理解的文本。例如&#xff0c;它可以搜索 DLL 和可执行文件以查看它们是否恰好包含字符串。 下面的代码中显示的ListFiles 方法完成了大部分工作。 …...

C++设计模式

C设计模式 什么是 C 设计模式&#xff1f;设计模式的用途设计模式的核心原则设计模式的分类 1. 创建型设计模式1.1 单例模式&#xff08;Singleton Pattern&#xff09;1.2 工厂方法模式&#xff08;Factory Method Pattern&#xff09;1.3 抽象工厂模式&#xff08;Abstract F…...

LM芯片学习

1、LM7805稳压器 https://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn1852815231102873600&utm_sourcewechat_sessionhttps://zhuanlan.zhihu.com/p/626577102?utm_campaignshareopn&utm_mediumsocial&utm_psn18528…...

使用 MyBatis-Plus Wrapper 构建自定义 SQL 查询

前言 MyBatis-Plus (MP) 是一款基于 MyBatis 的增强工具&#xff0c;它简化了数据库操作&#xff0c;提供了诸如自动分页、条件构造器等功能&#xff0c;极大地提高了开发效率。其中&#xff0c;Wrapper 条件构造器是 MP 的核心功能之一&#xff0c;它允许开发者以链式调用的方…...

C# OpenCvSharp DNN 实现百度网盘AI大赛-表格检测第2名方案第一部分-表格边界框检测

目录 说明 效果 模型 项目 代码 frmMain.cs YoloDet.cs 参考 下载 其他 说明 百度网盘AI大赛-表格检测的第2名方案。 该算法包含表格边界框检测、表格分割和表格方向识别三个部分&#xff0c;首先&#xff0c;ppyoloe-plus-x 对边界框进行预测&#xff0c;并对置信…...

手分割数据集labelme格式505张1类别

数据集格式&#xff1a;labelme格式(不包含mask文件&#xff0c;仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数)&#xff1a;505 标注数量(json文件个数)&#xff1a;505 标注类别数&#xff1a;1 标注类别名称:["hands"] 每个类别标注的框数&#xf…...

2012年西部数学奥林匹克试题(几何)

2012/G1 △ A B C \triangle ABC △ABC 内有一点 P P P, P P P 在 A B AB AB, A C AC AC 上的投影分别为 E E E, F F F, 射线 B P BP BP, C P CP CP 分别交 △ A B C \triangle ABC △ABC 的外接圆于点 M M M, N N N. r r r 为 △ A B C \triangle ABC △ABC 的内…...

GB28181系列三:GB28181流媒体服务器ZLMediaKit

我的音视频/流媒体开源项目(github) GB28181系列目录 目录 一、ZLMediaKit介绍 二、 ZLMediaKit安装、运行(Ubuntu) 1、安装 2、运行 3、配置 三、ZLMediaKit使用 一、ZLMediaKit介绍 ZLMediaKit是一个基于C11的高性能运营级流媒体服务框架&#xff0c;项目地址&#xf…...

【微服务】SpringBoot 整合Redis Stack 构建本地向量数据库相似性查询

目录 一、前言 二、向量数据库介绍 2.1 什么是向量数据库 2.2 向量数据库特点 2.3 向量数据库使用场景 三、常用的向量数据库解决方案 3.1 Milvus 3.1.1 Milvus是什么 3.1.2 Milvus主要特点 3.2 Faiss 3.2.1 Faiss是什么 3.2.2 Faiss主要特点 3.3 Pinecone 3.3.1 …...

神州数码DCME-320 online_list.php存在任意文件读取漏洞

免责声明: 本文旨在提供有关特定漏洞的深入信息,帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步,未经授权访问系统、网络或应用程序,可能会导致法律责任或严重后果。因此,作者不对读者基于本文内容所采取的任何行为承担责任。读者在…...

Shadcn UI 实战:打造可维护的企业级组件库

"我们真的需要自己写一套组件库吗&#xff1f;"上周的技术评审会上,我正在和团队讨论组件库的选型。作为一个快速发展的创业公司,我们既需要高质量的组件,又想保持灵活的定制能力。在对比了多个方案后,我们选择了 shadcn/ui 这个相对较新的解决方案。 说实话,最开始…...

C#速成(GID+图形编程)

常用类 类说明Brush填充图形形状,画刷GraphicsGDI绘图画面&#xff0c;无法继承Pen定义绘制的对象直线等&#xff08;颜色&#xff0c;粗细&#xff09;Font定义文本格式&#xff08;字体&#xff0c;字号&#xff09; 常用结构 结构说明Color颜色Point在平面中定义点Rectan…...

CMD使用SSH登陆Ubuntu

1.确认sshserver是否安装好 ps -e | grep sshd 450 ? 00:00:00 sshd 2、如果看到sshd那说明ssh-server已经启动了 其实在/etc/ssh下有一个sshd_config 文件。对这个文件进行修改vim sshd_config。 往文件中添加如下内容&#xff1a; Port 22 Protocol 2 PermitRootLogin yes P…...

Fay环境安装及使用

一、项目源码 代码地址 &#xff1a; Fay 2D数字人源码地址&#xff1a;xuniren LLM用是清华开源的ChatGLM源码地址&#xff1a;ChatGLM-6B 模型地址chatglm2-6b-int4 &#xff08;大模型的安装直接参考了我的另一篇文章&#xff1a;ChatGLM2-6B-int4的…...

重写 `equals` 和 `hashCode` 的一致性

重写 equals 和 hashCode 的一致性 在 Java 中&#xff0c;当我们重写 equals 方法时&#xff0c;通常需要同时重写 hashCode 方法&#xff0c;以确保对象在逻辑上相等时&#xff0c;其哈希值也相等。这是一种重要的契约&#xff08;contract&#xff09;&#xff0c;主要用于…...

【游戏设计原理】14 - MDA:游戏的机制、运行和体验

1. 学习、分析并总结 MDA 原理 MDA (Mechanics, Dynamics, and Aesthetics) 是一种用来分析和理解游戏设计的框架&#xff0c;由 Marc LeBlanc, Robin Hunicke, 和 Robert Zubek 提出。这个框架将游戏分解为三个核心要素&#xff1a; Mechanics&#xff08;机制&#xff09;&…...

鸿蒙Next创建自定义组件总结

一、引言 在鸿蒙Next开发中&#xff0c;自定义组件是构建高效、可维护UI的重要组成部分。它具有可组合、可重用以及数据驱动UI更新等特点&#xff0c;能帮助开发者更好地实现代码复用、业务逻辑与UI分离等目标。本文将详细总结创建自定义组件的相关知识&#xff0c;包括其基本…...