参考:深入理解Qt
Qt::WA_TransparentForMouseEvents
设置鼠标事件透传到底层子孙,如果在当前类的构造函数中设置了此值,那么当前类对象就会收不到mousePressEvent
等消息处理函数。
QT信号槽机制是QT中一个非常重要的特性,它是一种对象间通信的机制,允许一个对象(信号源)在某些条件下发出一个信号,而其他对象(槽)则在收到该信号时做出响应。
信号槽机制的实现原理如下:
定义信号和槽函数:在一个对象中定义一个信号,并在其他对象中定义一个槽函数。信号和槽函数都是C++函数,它们遵循特定的命名规则。信号函数通常是在需要发出信号的类中定义,而槽函数通常是在需要处理信号的类中定义。
连接信号和槽:当一个信号被发出时,需要将该信号连接到一个或多个槽函数上。这可以通过使用QObject::connect()函数来实现。这个函数接受三个参数:信号源、信号和槽函数。一旦信号和槽函数被连接,当信号源发出信号时,与该信号连接的所有槽函数都会被自动调用。
发射信号:当信号源需要发出信号时,可以使用emit关键字来调用信号函数。当信号函数被调用时,与该信号连接的所有槽函数都会被自动调用。
处理信号:当槽函数被调用时,它可以访问信号参数,并对信号做出响应。槽函数可以是任意C++函数,它们可以执行任意操作,包括修改信号源的状态、显示消息等。
在信号槽机制中,信号是Qt框架提供的一种特殊的函数,它不返回任何值,但可以有零个或多个参数。槽函数是普通的C++函数,与任何其他函数一样,它们接受参数并返回值。信号槽机制的关键是使用QObject::connect()函数来连接信号和槽函数,这样一旦信号被发出,所有与之连接的槽函数都会被自动调用,从而实现了对象间的通信。
在Qt中,信号槽机制是通过元对象系统(Meta-Object System)来实现的。元对象系统是一个运行时类型信息(RTTI)机制,用于在运行时查找和操作对象的属性、信号和槽等信息。每个QObject及其派生类都具有一个元对象(meta-object),该元对象在运行时创建,它包含了类的名称、父类的名称、对象的属性、信号和槽等信息。
当QObject及其派生类中定义了信号和槽函数时,Qt会使用元对象系统来注册这些信号和槽函数,并为它们生成元对象代码。这些元对象代码在应用程序启动时被执行,它们将信号和槽函数注册到元对象系统中,从而使它们能够在运行时被动态地查找和连接。
在运行时,当信号被发出时,元对象系统会根据信号源的元对象来查找与该信号相匹配的槽函数。如果找到了匹配的槽函数,元对象系统会调用该槽函数,并将信号参数传递给它。如果没有找到匹配的槽函数,那么该信号就被忽略。
在连接信号和槽时,QObject::connect()函数的内部实现使用了Qt的元对象系统。当调用该函数时,Qt会在元对象系统中查找信号和槽函数,并将它们连接起来。在连接过程中,Qt还会检查信号和槽函数的参数类型和数量是否匹配,以及它们是否都是可调用的(即可被调用的对象和方法是否存在)。
值得注意的是,由于元对象系统的存在,Qt信号槽机制可以在编译期外连接,这使得Qt信号槽机制成为了Qt框架的核心功能之一。同时,这也让开发者能够更加灵活地编写代码,将信号和槽函数连接在不同的对象之间,从而实现对象之间的通信。
如果信号和槽函数处在不同的线程中,Qt信号槽机制会根据以下情况进行不同的处理:
如果信号和槽函数处在同一个线程中,那么它们的连接和调用与单线程模式下是一样的。
如果信号和槽函数处在不同的线程中,并且它们的父对象是同一个,那么它们的连接和调用与单线程模式下也是一样的。因为它们的父对象是同一个,所以它们会被同一个线程管理,这样就避免了线程安全问题。
如果信号和槽函数处在不同的线程中,并且它们的父对象不同,那么Qt会自动将信号的参数复制到槽函数所在的线程中,并在槽函数所在的线程中执行槽函数。这个过程是通过Qt提供的事件循环机制实现的。
具体来说,当信号发出时,Qt会将信号加入到目标对象所在的线程的事件队列中。当目标对象所在的线程开始处理事件时,它会检查事件队列中是否有新的信号需要处理,如果有,那么它就会执行与该信号相关联的槽函数。在执行槽函数之前,Qt会先将信号的参数复制到槽函数所在的线程中,这样就保证了线程安全性。
signals:
void testSignal(int a);
中的signals是一个宏,定义如下:
#ifdef signals
# define signals public __attribute__((annotate("qt_signal")))
#endif
当您点击Qt窗口时,Qt会在您点击的位置生成一个事件并将其发送到事件循环。事件循环是Qt应用程序中负责处理所有事件的主循环。它等待事件,并在接收到事件时对其进行处理。
当事件循环接收到鼠标点击事件时,它会检查该事件的类型,以确定如何处理该事件。如果该事件是鼠标单击事件,则事件循环会检查该单击是否发生在一个QWidget上(例如按钮)。
如果该单击发生在QWidget上,则事件循环会调用QWidget的事件处理函数,该函数会对该事件进行处理。例如,如果该QWidget是一个按钮,则该函数可能会触发按钮的单击信号,从而触发该信号的相关槽函数。槽函数是用户定义的函数,其中包含用户希望在该事件发生时执行的代码。
Qt信号槽或者QML中使用自定义类型,需要注意使用qRegisterMetaType对自定义类型进行注册
在当前类的顶部包含:#include
,构造函数中加入代码:qRegisterMetaType
Q_OBJECT 是 Qt 框架中的一个宏定义,用于在类的声明中标记该类需要使用 Qt 的元对象系统(Meta-Object System)。使用 Q_OBJECT 宏定义后,编译器会在编译期自动生成元对象代码,包括信号(signal)和槽(slot)的注册、元对象信息的注册等等。
具体来说,使用 Q_OBJECT 宏定义后,编译器会为该类生成一个 QMetaObject 对象,该对象包含了该类的元对象信息,包括类名、信号和槽的名称、参数类型等等。这些信息可以通过 QObject::metaObject() 函数获取到。
此外,使用 Q_OBJECT 宏定义后,还可以在该类中使用信号和槽,使用 emit 关键字来发射信号,使用 connect 函数将信号和槽连接起来。这些功能都是通过 Qt 的元对象系统实现的。
需要注意的是,使用 Q_OBJECT 宏定义的类必须直接或间接继承自 QObject 类。
完整看一遍:线程和 QObject
class Thread : public QThread {
void run() {
QObject *obj = new QObject(this); // 错误!
}
};
this对象是属于创建这个线程的线程,而obj(设置this为其父)是属于这个线程的,那就跟它的父this不是归属于同一个线程,这是不允许的。
QObject及其所有子类都不是线程安全的(但都是可重入的)。因此,你不能有两个线程同时访问一个QObject对象,除非这个对象的内部数据都已经很好地序列化(例如为每个数据访问加锁)。
这里有两个概念:
调用QObject::moveToThread()函数。该函数会改变一个对象及其所有子对象的线程依附性。
QObject被设计成在一个单线程中创建与使用,因此,在一个线程中创建一个对象,而在另外的线程中调用它的函数,这样的行为不能保证工作良好。
重点阅读:深入理解Qt——事件循环
QT 事件的传递先后顺序
用法1:
void widget::on_pushButton_clicked()
{
........ 需要等待100毫秒的时间.等待某个条件成立
//事件循环: 在指定时间后执行某件事. 不卡UI界面
QEventLoop loop;
QTimer::singleShot(100, &loop, SLOT(quit()));
loop.exec();
.........
继续执行代码.
}
用法2:
QEventLoop loop;
void widget::on_pushButton1_clicked()
{
........ 需要等待某个条件成立
loop.exec();
.........
继续执行代码.
}
//槽函数: 等待的条件满足时进入
void widget::on_timeclicked()
{
..........
loop.exit(); //退出事件循环
}
因为QEventLoop::exec()开启了一个新的事件循环来分发事件,而且相同线程上的所有事件循环采用同一个事件调度器。
int QCoreApplication::exec()
主事件循环接收来自系统的事件,并转换为QT的事件消息,并把这些事件分发到对应的应用程序窗口上。
bool QCoreApplication::notify(QObject *receiver, QEvent *event)
5种处理事件的方法:
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
把事件event投递到对象receiver所在事件队列中,并立即返回。
对于deleteLater的用法,官方描述如下:
The object will be deleted when control returns to the event
loop. If the event loop is not running when this function is
called (e.g. deleteLater() is called on an object before
QCoreApplication::exec()), the object will be deleted once the
event loop is started. If deleteLater() is called after the main event loop
has stopped, the object will not be deleted.
Since Qt 4.8, if deleteLater() is called on an object that lives in a
thread with no running event loop, the object will be destroyed when the
thread finishes.
实现如下:
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
它是线程安全的删除QObject对象的方法。
Qobj->deleteLater()调用后,Qobj这个对象真正被执行删除的时候是在(当前这个线程?)重新回到事件循环的时候处理的。如果在事件循环未启动前就调用deleteLater,那么这个对象会在事件循环一启动就会被删除。如果在事件循环结束后,再调用deleteLater,那么这个对象将不会被删除。
如果在一个子线程中,这个子线程没有运行事件循环,那么这个对象的删除,将在子线程结束时删除。而对于整个程序而言,主线程都会有事件循环(因为,return QApplication::exec()),所以主线程中删除对象不会存在这个情形。
模型(Model): 模型负责管理应用程序的数据和业务逻辑。它独立于用户界面,提供了数据的获取、存储、修改等功能。模型通常是应用程序的核心部分,通过提供接口供视图和控制器访问和操作数据。
视图(View): 视图负责显示模型中的数据以及用户界面的外观和布局。它根据模型的数据进行更新,用户通过视图来看到数据的呈现。视图不处理业务逻辑,只负责显示和用户交互。
控制器(Controller): 控制器是用户界面和模型之间的中介,处理用户输入并更新模型和视图。它将用户的操作转化为模型的更新和视图的刷新。控制器使得模型和视图可以解耦,从而实现更好的代码组织和维护。
#include
// 模型类
class Model : public QObject {
Q_OBJECT
public:
explicit Model(QObject *parent = nullptr) : QObject(parent) {
data = "Hello, MVC!";
}
QString getData() const {
return data;
}
private:
QString data;
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Model model;
// 视图类
QLabel label;
label.show();
// 控制器
QObject::connect(&label, &QLabel::linkActivated, [&model, &label]() {
label.setText(model.getData());
});
label.setText("Click Me");
return app.exec();
}
#include "main.moc"
QT 中的模型/视图机制是一种用于管理数据的框架,可以将数据和其显示方式分离,并提供一种通用的接口来访问和操作数据。在 QT 中,模型/视图机制通常使用 QAbstractItemModel、QAbstractListModel、QAbstractTableModel 等类来实现。
模型负责提供数据,视图负责将数据显示在界面上,并允许用户进行操作。视图通常包括 QListView、QTableView、QTreeView 等类,用于显示不同类型的数据。当模型数据发生变化时,模型会自动发送信号给视图,并通知视图进行更新。同时,视图也可以通过发送信号给模型来请求数据,例如获取某一行、某一列的数据等。
QT 中的模型/视图机制支持多种不同的数据源,例如内存中的数据、数据库中的数据、XML 文件中的数据等。此外,QT 中还提供了一些常用的模型实现,例如 QDirModel、QFileSystemModel、QStandardItemModel 等,用于快速创建常见的数据模型。
QFrame 和 QWidget 都是 Qt 中的 GUI 组件,但是它们有一些区别:
继承关系:QFrame 继承自 QWidget,所以 QFrame 具有 QWidget 的所有功能。
功能:QFrame 提供了一个简单的框架,可以作为其他控件的容器。它还可以用来绘制简单的图形,如线条。QWidget 没有这样的功能,但是提供了基础的 GUI 组件功能,如设置尺寸和位置等。
外观:QFrame 可以有边框和背景颜色,因此外观更加丰富。QWidget 只有背景颜色,没有边框。
通常,当需要一个简单的框架时,使用 QFrame,当需要基础的 GUI 组件功能时,使用 QWidget。
参考:https://www.kancloud.cn/kancloud/qt-study-road-2/99456
答,采用函数指针确定连接哪个信号。
槽函数的参数可以少于信号的参数。
也可以。唯一的情况就是槽函数参数带有默认参数,除去默认参数外,槽函数的参数必须小于等于信号的参数。
什么是 QT?QT 的优势是什么?
QT 的事件机制是什么?什么是事件循环?
QT 中的信号和槽是什么?如何使用信号和槽连接不同的对象?
QT 中的多线程机制是什么?如何在 QT 中使用多线程?
QT 中常用的布局管理器有哪些?如何使用布局管理器来布置窗口控件?
QT 中的界面控件有哪些?如何使用界面控件来实现特定的功能?
QT 中如何实现自定义控件?
QT 中的文件系统和网络编程是如何实现的?如何使用 QT 进行文件读写和网络通信?
QT 中如何进行国际化和本地化?
QT 中如何进行调试和性能优化?
当然,除了这些问题,面试官可能还会问到一些其他的 QT 相关问题,比如 QT 中的绘图机制、动画效果、插件机制等等。建议在面试前认真复习 QT 的相关知识点,并做好充分的准备。