知识不是单独的,一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏:Visual Studio。
还整了一个如何相互之间调用函数的文章,感兴趣可以看:【Visual Studio】Qt 在其他 cpp 文件中调用主工程下文件中的函数。
文章目录
- 创建一个原始工程名字为 `A`
- `A.ui`
- `A.h`
- `A.cpp`
- 修改后
- `A.h`
- `A.cpp`
- `test.h`
- `test.cpp`
- Ref.
主界面工程为 A
,添加的文件名字为 test
,目标是在 test
文件里操作 A
工程里的 ui
控件。
简洁版直接看这个截图就行,大意就是将老的实例化 ui
变成指针,将地址传递给 p_ui
,然后通过调用 p_ui
来间接调用 ui
。
名称解释:
c_test
表示新建的类p_ui
表示新建的类里的指针类型的成员变量testFun()
表示新建的类的函数mc_test
实例化的类,用来调用新建的c_test
的类
详细版可以看下边这些。
创建一个原始工程名字为 A
为了更好地演示,首先创建一个原始工程,名字为 A
,并打印一段文字。效果如下图所示:
工程的四个项目文件分别如下:
A.ui
A.h
// A.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_A.h"
class A : public QMainWindow
{
Q_OBJECT
public:
A(QWidget *parent = nullptr);
~A();
private:
Ui::AClass ui;
};
类定义:class A : public QMainWindow
指示 A
类是一个继承自 Qt 的主窗口类 QMainWindow
的类。通过继承,A
类获得了 QMainWindow
的功能,并可以在其基础上进行扩展。
Q_OBJECT
: 这是一个宏,用于启用 Qt 的元对象系统(Meta-Object System,MOC)。元对象系统是 Qt 中一种用于实现信号与槽机制,反射和其他元编程功能的机制。为了使用这些功能,类必须包含 Q_OBJECT 宏。
构造函数:A(QWidget *parent = nullptr);
是 A
类的构造函数。构造函数在创建类的实例时被调用。它的参数 QWidget *parent
表示构造函数的父类,这里设置为 nullptr
,表示没有父类。
析构函数:~A();
是 A
类的析构函数。析构函数在类的实例被销毁时被调用。在析构函数中,通常进行资源的释放和清理工作。
私有成员变量:Ui::AClass ui;
是一个私有成员变量,它是通过 UI
文件自动生成的用户界面类的实例。通过这个变量,您可以访问和操作 UI
文件中定义的组件和布局。
双冒号
::
表示作用域解析运算符(Scope Resolution Operator)。它有两个主要的用途:
- 命名空间成员的访问:在 C++ 中,可以使用命名空间将一组相关的类、函数、变量等封装在一起。双冒号
::
用于访问命名空间中的成员。在代码中,Ui::AClass
中的Ui
表示一个命名空间,AClass
则是在Ui
命名空间中定义的一个类。因此,Ui::AClass
表示访问位于Ui
命名空间中的AClass
类。- 静态成员的访问:在类中,可以定义静态成员(Static Members),这些成员属于类本身,而不是类的实例。双冒号
::
用于访问类的静态成员。在代码中,Ui::AClass
中的Ui
表示一个命名空间或类,而AClass
则是在这个命名空间或类中定义的一个静态成员。因此,Ui::AClass
表示访问类Ui
中的静态成员AClass
。在上述代码中,
Ui::AClass
表示访问命名空间或类Ui
中的类AClass
。这个类是通过Qt
设计师生成的,用于描述与窗体A
相关联的用户界面。总结来说,双冒号
::
用于在 C++ 中访问命名空间中的成员或类的静态成员,帮助准确定位所需的符号,避免命名冲突,并使代码更加清晰和模块化。
Ui::AClass
表示一个名为 AClass
的用户界面类。它是通过 Qt 的用户界面设计器创建的类,用于描述窗体的布局和组件。通常,当你使用 Qt 设计师(Qt Designer)创建一个用户界面时,它会生成一个对应的头文件(通常以 .h
为扩展名),其中包含了一个名为 Ui::ClassName
的类。ClassName
是你在设计师中指定的窗体的类名,可以是你创建的窗体的名称或其他自定义名称。在上述示例代码中,Ui::AClass
表示在 Qt 设计师中创建的与窗体 A
相关联的用户界面类。它描述了 A
窗体的布局和组件,包括在设计师中创建的各种控件(如按钮、标签、文本框等)的属性和布局信息。
#include "ui_A.h"
Ui::AClass
是通过包含名为 ui_A.h
的头文件来定义的。在这个头文件中,Qt 设计师自动生成了与 A
窗体相关的用户界面类的声明和定义。通过包含 ui_A.h
头文件,你可以在 A
类中创建一个名为 ui
的对象,它是 Ui::AClass
类的一个实例。通过这个 ui
对象,你可以访问并操作在设计师中创建的窗体组件。
例如,你可以使用 ui
对象来设置窗体中的控件的属性,或在代码中对控件进行操作。例如,如果你在设计师中创建了一个名为 pushButton
的按钮控件,你可以使用 ui.pushButton
来访问和操作该按钮控件。
总的来说,Ui::AClass
是一个自动生成的用户界面类,它用于描述与窗体 A
相关联的用户界面,并通过 ui
对象提供对窗体组件的访问和操作。这种分离窗体逻辑与用户界面描述的设计方式,使得代码更加模块化和易于维护。
A.cpp
// A.cpp
#include "A.h"
A::A(QWidget *parent) : QMainWindow(parent)
{
ui.setupUi(this);
ui.textBrowser->insertPlainText("This is a text from A.cpp!\n");
}
A::~A()
{}
这段代码定义了类 A
的构造函数和析构函数的实现,其中在构造函数中通过 ui.setupUi(this);
将用户界面与窗体关联。
A::A(QWidget *parent) : QMainWindow(parent)
:这是类 A
的构造函数的实现部分。在构造函数中,首先调用了父类 QMainWindow
的构造函数,使用 parent
参数来初始化基类。
接着,通过 ui.setupUi(this);
调用了 ui
对象的 setupUi
函数,将 ui
对象与当前的窗体对象 this
(即类 A
的实例)关联起来。这样,ui
对象就可以用来访问与窗体 A
相关联的用户界面组件。
A::~A()
:这是类 A
的析构函数的实现部分。析构函数在对象被销毁时自动调用,用于释放对象的资源。在这里,析构函数不做任何额外的操作,因为没有需要手动释放的资源。
至于为什么是
A::A(QWidget *parent)
,而不是A:A(QWidget *parent)
?
这是因为对于 C++ 中的构造函数和析构函数,正确的语法是使用两个冒号::
来指明函数所属的类,而不是一个冒号:
。这是C++中的命名空间解析操作符(scope resolution operator)。所以,在构造函数的定义中,应该是A::A()
而不是A:A()
。同样,析构函数的定义应该是A::~A()
而不是A:~A()
。
在这里,A::
表示A
类中的成员函数,A(QWidget *parent)
表示构造函数的名称,~A()
表示析构函数的名称。函数名前面的A::
是用来限定这些函数是属于A
类的。
如果在构造函数或析构函数的定义中使用了错误的语法(例如A:A()
或A:~A()
),编译器会报错并指出无法找到匹配的函数声明。
修改后
接下来将创建一个 test.h
和一个 test.cpp
文件,并实现在 test.cpp
中调用 ui 中的控件 textBrowser
,并打印出来一段文字标记来自 test.cpp
。最终效果如下图所示:
A.h
// A.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_A.h"
#include "test.h" // new code
class A : public QMainWindow
{
Q_OBJECT
public:
A(QWidget *parent = nullptr);
~A();
private:
Ui::AClass *ui; // change code
c_test mc_test; // new code
};
修改后的 A.h
相较于之前的,添加了 #include "test.h"
,这是一个预处理指令,用于包含名为 test.h
的头文件。这意味着您在 A.h
中引入了 test.h
文件中定义的内容,可能是另一个自定义类或者功能模块。我们这里属于引入了自定义类 test
。
Ui::AClass *ui;
:这是类 A
的一个私有成员变量的修改。原来的代码中使用的是 Ui::AClass ui;
,表示 ui
是一个 Ui::AClass
类型的对象。而在这个修改后的代码中,使用了 Ui::AClass *ui
;,表示 ui
是一个 Ui::AClass
类型的指针。这个修改是为了在类 A
中使用指针来管理 ui
对象的生命周期和资源释放,方便咱们在 test.h
中访问。
c_test mc_test;
:这是一个新的类成员变量,它是通过 test.h
头文件定义的 c_test
类的实例。现在在 A
类中拥有一个名为 mc_test
的 c_test
类对象。mc_test
可以用来访问和操作 c_test
类中的成员。
这些变化是希望在 A
类中使用名为 c_test
的自定义类,并且想要通过指针动态地管理 ui
成员变量。
A.cpp
// A.cpp
#include "A.h"
A::A(QWidget *parent) : QMainWindow(parent)
{
ui->setupUi(this);
ui->textBrowser->insertPlainText("This is a text from A.cpp!\n");
mc_test.p_ui = ui; // new code
mc_test.testFun(); // new code
}
A::~A()
{}
mc_test.p_ui = ui;
:这是新添加的代码行,它将 ui
指针赋值给名为 mc_test
的 c_test
类对象 p_ui
成员变量。根据代码可以看到,mc_test
对象具有一个成员变量 p_ui
,用于存储 ui
指针的值。
mc_test.testFun();
:这也是新增的代码行,它调用 c_test
类对象 mc_test
的 testFun()
成员函数。根据代码的意思,testFun()
函数是 c_test
类中的一个成员函数,根据函数定义(看 test.cpp
文件)也可以知道,它利用了存储在 p_ui
成员变量中的 ui
指针,以实现与 UI 相关的功能。
需要注意的是,因为对 A.h
进行了修改,将 ui
成员变量从对象变为了指针。因此,在 A.cpp
中使用 ui
时,需要使用 ui->
来访问其成员,而不再是 ui.
,例如 ui->setupUi(this)
和 ui->textBrowser->insertPlainText("This is a text from A.cpp!\n")
。
test.h
// test.h
#ifndef TEST_H
#define TEST_H
#include "ui_A.h"
class c_test
{
public:
Ui::AClass* p_ui;
void testFun();
};
#endif
#include "ui_A.h
:这一行包含名为 ui_A.h
的头文件,它是通过 Qt 的 UI 设计器从 A.ui
生成的头文件。这个头文件中包含了 UI 文件中定义的类和组件。
Ui::AClass* p_ui;
:这是 c_test
类的成员变量,它是一个指向 Ui::AClass
类型的指针。根据之前的代码,Ui::AClass
是通过 UI 文件生成的用户界面类,而 p_ui
指针将用于存储 A
类中的 ui
指针,以便在 c_test
类中访问和操作 A
类的UI组件。
void testFun();
:这是 c_test
类的成员函数声明。根据之前在 A.cpp
中的调用 mc_test.testFun()
,这个函数被用于实现一些与 UI 相关的功能,使用了存储在 p_ui
成员变量中的 ui
指针。
现在,可以在 test.cpp
中实现 test.h
中声明的成员函数,以及根据需要与ui进行交互。
test.cpp
// test.cpp
#include "test.h"
void c_test::testFun()
{
p_ui->textBrowser->insertPlainText("This is a text from test.cpp!\n");
}
#include "test.h"
:这是包含 test.h
头文件,这样就可以访问 test
类的声明和成员函数定义。
void c_test::testFun()
:这是 c_test
类的成员函数 testFun()
的实现。根据代码的内容,这个函数将在 A
类的 UI 组件 textBrowser
中插入一行文本。这里的实现通过使用 p_ui
成员变量来访问 A 类中的 ui
指针,因为 p_ui
指向 Ui::AClass
类型,而 textBrowser
是 Ui::AClass
类中的一个成员。
总结一下,test.cpp
中的代码通过使用存储在 p_ui
成员变量中的 ui
指针,成功在 A
类的 textBrowser
中插入了一行文本。这样,c_test
类就可以与 A
类的 UI 组件进行交互。
Ref.
- Qt中,在另一cpp文件操作ui界面的相关控件
- [Qt] [UI] 多个类中操作同一个UI界面