QCoreApplication

发布时间:2024年01月18日

首先介绍下qApp

qApp是个宏定义,目的就是获取app实例

主函数里面调用的是:

  1. QCoreApplication app(argc, argv);
    qApp就是QCoreApplication类型的指针,实际指向app
  2. QGuiApplication app(argc, argv);
    qApp就是QGuiApplication类型的指针,实际指向app
  3. QApplication app(argc, argv);
    qApp就是QApplication类型的指针,实际指向app

看下原理:

qcoreapplication.h中

#define qApp QCoreApplication::instance()
static QCoreApplication *self;
static QCoreApplication *instance() { return self; }

QCoreApplication构造

QCoreApplication::QCoreApplication(int &argc, char **argv)
    : QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
{
    d_func()->q_ptr = this;
    d_func()->init();
    QCoreApplicationPrivate::eventDispatcher->startingUp();
}

void QCoreApplicationPrivate::init()
{
    Q_Q(QCoreApplication);

    //如果self已经存在,直接中断退出程序
    Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");

    //self这里指向q_ptr即this
    QCoreApplication::self = q;
}
结合构造函数我们知道:
QCoreApplication app(argc, argv);定义app后self就指向了app;
并且一个程序就只能定义一个app对象。

qguiapplication.h中

#include <QtCore/qcoreapplication.h> //引用qcoreapplication.h

#if defined(qApp)
#undef qApp    //先取消qApp宏定义
#endif
#define qApp (static_cast<QGuiApplication *>(QCoreApplication::instance()))
 //重新定义qApp宏定义,指向QGuiApplication

#if defined(qGuiApp)
#undef qGuiApp
#endif
#define qGuiApp (static_cast<QGuiApplication *>(QCoreApplication::instance()))
 //多定义了个,qGuiApp宏定义,与qApp一样功能

qapplication.h中

#include <QtCore/qcoreapplication.h> //引用qcoreapplication.h
#include <QtGui/qguiapplication.h>  //qguiapplication.h

#if defined(qApp) 
#undef qApp   //先取消qApp宏定义
#endif
#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))
 //重新定义qApp宏定义QApplication

QCoreApplication构造流程

QCoreApplication::QCoreApplication(int &argc, char **argv)
    : QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
{
    d_func()->q_ptr = this;
    d_func()->init(); 
    QCoreApplicationPrivate::eventDispatcher->startingUp();
}
  1. 创建QCoreApplicationPrivate

QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint flags)
    : QObjectPrivate(),argc(aargc)
    , argv(aargv)
    , application_type(QCoreApplicationPrivate::Tty) //注意这里的app类型是tty
    , in_exec(false)
    , aboutToQuitEmitted(false)
    , threadData_clean(false)
{
    app_compile_version = flags & 0xffffff;
    QCoreApplicationPrivate::is_app_closing = false;
    QThread *cur = QThread::currentThread();
}

  1. 初始化QCoreApplicationPrivate
void QCoreApplicationPrivate::init()
{
    //这里断言,一个程序只能创建一个app,只能创建一个app对象
    Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
    QCoreApplication::self = q;

    auto thisThreadData = threadData.loadRelaxed();
    eventDispatcher = thisThreadData->eventDispatcher.loadRelaxed();

    if (!eventDispatcher)
        createEventDispatcher();//创建事件分发器
    Q_ASSERT(eventDispatcher);

    if (!eventDispatcher->parent()) {
        eventDispatcher->moveToThread(thisThreadData->thread.loadAcquire());
        eventDispatcher->setParent(q);
    }

    thisThreadData->eventDispatcher = eventDispatcher;
    eventDispatcherReady();
    //解析命令行参数
    processCommandLineArguments();

    qt_call_pre_routines();
    qt_startup_hook();
#ifndef QT_BOOTSTRAPPED
    if (Q_UNLIKELY(qtHookData[QHooks::Startup]))
        reinterpret_cast<QHooks::StartupCallback>(qtHookData[QHooks::Startup])();
#endif

    is_app_running = true; // No longer starting up.
}

创建事件分发器流程

void QCoreApplicationPrivate::createEventDispatcher()
{
    Q_Q(QCoreApplication);
    QThreadData *data = QThreadData::current();
    Q_ASSERT(!data->hasEventDispatcher());
    eventDispatcher = data->createEventDispatcher();
    eventDispatcher->setParent(q);
}

QAbstractEventDispatcher *QThreadData::createEventDispatcher()
{
    QAbstractEventDispatcher *ed = QThreadPrivate::createEventDispatcher(this);
    eventDispatcher.storeRelease(ed);
    ed->startingUp();
    return ed;
}

//根据平台创建不同的事件分发器
QAbstractEventDispatcher *QThreadPrivate::createEventDispatcher(QThreadData *data)
{
    Q_UNUSED(data);
#if defined(Q_OS_DARWIN)
    bool ok = false;
    int value = qEnvironmentVariableIntValue("QT_EVENT_DISPATCHER_CORE_FOUNDATION", &ok);
    if (ok && value > 0)
        return new QEventDispatcherCoreFoundation;
    else
        return new QEventDispatcherUNIX;
#elif !defined(QT_NO_GLIB)
    const bool isQtMainThread = data->thread.loadAcquire() == QCoreApplicationPrivate::mainThread();
    if (qEnvironmentVariableIsEmpty("QT_NO_GLIB")
        && (isQtMainThread || qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB"))
        && QEventDispatcherGlib::versionSupported())
        return new QEventDispatcherGlib;
    else
        return new QEventDispatcherUNIX;
#else
    return new QEventDispatcherUNIX;
#endif
}

//QEventDispatcherGlib
QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
    : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
{
}


QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
    : mainContext(context)
{
    //注册处理函数
    postEventSource = postEventSourceFuncs;
    socketNotifierSource = socketNotifierSourceFuncs;
    timerSource = timerSourceFuncs;
    idleTimerSource = idleTimerSourceFuncs;
}

static GSourceFuncs postEventSourceFuncs = {
    postEventSourcePrepare,
    postEventSourceCheck,
    postEventSourceDispatch,
    nullptr,
    nullptr,
    nullptr
};

static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
{
    GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
    source->lastSerialNumber = source->serialNumber.loadRelaxed();
    QCoreApplication::sendPostedEvents();
    source->d->runTimersOnceWithNormalPriority();
    return true; // i dunno, george...
}
void QCoreApplication::sendPostedEvents(QObject *receiver, int event_type)
{
    QThreadData *data = QThreadData::current();
    QCoreApplicationPrivate::sendPostedEvents(receiver, event_type, data);
}

static void setAttribute(Qt::ApplicationAttribute attribute, bool on = true);

设置和清除某属性,如果on为真,则设置属性attribute;否则清除该属性。

static bool testAttribute(Qt::ApplicationAttribute attribute);

返回是否设置了属性

static void setSetuidAllowed(bool allow);

设置了此标志后程序执行将会以拥有者的身份去执行。
如果allow为false(默认值),并且Qt检测到应用程序正在使用与实际用户id不同的有效用户id运行,则在创建QCoreApplication实例时将终止应用程序。
注意:强烈建议不要启用此选项,因为它会引入安全风险。

  1. 只有可以执行的二进制程序才能设定SUID权限
  2. 命令执行者要对该程序拥有 x (执行)权限
  3. 命令执行者在执行该程序时获得该程序文件属主的身份(在执行程序的过程中灵魂附体为文件的属主)
  4. SetUID权限只在该程序执行过程中有效,也就是说身份改变只在程序执行过程中有效
static bool isSetuidAllowed()
返回是否设置了uid属性

发送事件

static bool sendEvent(QObject *receiver, QEvent *event);

使用notify()函数将事件事件直接发送给接收者。同步执行,返回从事件处理程序返回的值。
事件发送后不会被删除。通常的方法是在栈上创建事件

 QMouseEvent event(QEvent::MouseButtonPress, pos, 0, 0, 0);
 QApplication::sendEvent(mainWindow, &event);

static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);

将事件事件(以对象接收者作为事件接收者)添加到事件队列并立即返回。异步执行。
post事件队列将获得事件的所有权,并在事件被发布后将其删除。事件必须在堆上分配
在事件发布之后再访问它是不安全的。

 QMouseEvent *event = new QMouseEvent(QEvent::MouseButtonPress, pos, 0, 0, 0);
 QApplication::postEvent(mainWindow, &event);

static void sendPostedEvents(QObject *receiver = nullptr, int event_type = 0);

立即调度所有先前在QCoreApplication::postEvent()中排队的事件,这些事件属于对象接收者receiver,并且事件类型为event_type。
注意:这个方法必须从其QObject参数receiver所在的线程中调用。

应用程序相关

static QString applicationDirPath();//返回程序所在目录
static QString applicationFilePath();//返回程序路径
static qint64 applicationPid() ;//返回当前程序的进程号
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug()<<a.applicationDirPath();
    qDebug()<<a.applicationFilePath();
    qDebug()<<a.applicationPid();
    return a.exec();
}
输出:
"E:/workspace/debug"
"E:/workspace/debug/guitest.exe"
5272

设置程序名称,公司名称,版本号信息

一般用不到这些函数

    static void setOrganizationDomain(const QString &orgDomain);
    static QString organizationDomain();
    static void setOrganizationName(const QString &orgName);
    static QString organizationName();
    static void setApplicationName(const QString &application);
    static QString applicationName();//不设置,默认返回程序名称
    static void setApplicationVersion(const QString &version);
    static QString applicationVersion();

返回命令行参数

[static] QStringList QCoreApplication::arguments()函数
返回命令行参数,前提构造时必须传入argc和argv

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);//这里传入了argc和argv
    qDebug()<<a.arguments();
      return a.exec();
}
./demo.exe arg1 arg2 3 4
输出:
("E:\workspace\\debug\demo.exe", "arg1", "arg2", "3", "4")

运行时库路径的设置

static void setLibraryPaths(const QStringList &);

设置运行时库所搜的路径。所有现有的路径将被删除,路径列表将由paths中给出的路径组成。

static QStringList libraryPaths();

返回运行时搜索的库路径

static void addLibraryPath(const QString &);

添加运行时库所搜的路径

static void removeLibraryPath(const QString &);

移除某个运行时的搜索路径

多语言翻译

static inline QString tr(const char *s, const char *c = nullptr, int n = -1)

返回s的翻译版本
s,需要翻译的字符串
c,消歧字符串
n,包含复数的字符串的值n;
如果没有合适的翻译字符串可用,则返回QString::fromUtf8(sourceText)。

此函数包含的字符串将作为需要翻译的字符串,被lupdate收集到翻译文件中(见下面所述)

新增翻译文件

1.新增要生成的翻译文件
pro文件里面添加TRANSLATIONS = zh.ts en.ts,新增要生成翻译文件zh.ts en.ts

2.生成翻译文件
选择:qt creator工具->外部->qt语言家->lupdate(或者手动执行lupdate + pro文件)
lupdate根据pro配置自动搜索生成需要翻译的文件: zh.ts en.ts (此时的文件都是未翻译的文件)

3.翻译
使用qt Linguist打开ts文件完成翻译并保存即可
打开的时候会让你选择源字符串语言类型和目标语言类型
zh.ts里面的源字符串翻译成中文
en.ts里面的源字符串翻译成英文
未翻译的字符串未undefined状态,可以手动文本方式打开ts文件查看

4、生成二进制翻译文件
选择:qt creator工具->外部->qt语言家->lrelease
生成二进制的zh.qm,en.qm文件

5.使用翻译文件
QTranslator的installTranslator函数装载翻译文件

#include <QTranslator>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTranslator translator;
    translator.load("zh.qm");
    translator.load("en.qm");//可以加载多个,翻译时先从最近加载的找,找到就返回
    a.installTranslator(&translator);
    return a.exec();
}

static bool installTranslator(QTranslator * messageFile);

将翻译文件translationFile添加到要用于翻译的翻译文件列表。
可以安装多个翻译文件。
搜索翻译的顺序与它们的安装顺序相反,因此首先搜索最近安装的翻译文件,最后搜索安装的第一个翻译文件。
一旦找到包含匹配字符串的翻译,搜索就会停止。

并且其会生成一个LanguageChange事件(LanguageChange事件只会发给widget类型的对象)

QWidgetm默认没有处理QEvent::LanguageChange事件
bool QWidget::event(QEvent *event)
{
...
case QEvent::LanguageChange:
        changeEvent(event);//调用changeEvent
        {
            QList<QObject*> childList = d->children;
            for (int i = 0; i < childList.size(); ++i) {
                QObject *o = childList.at(i);
                if (o)
                    QCoreApplication::sendEvent(o, event);
            }
        }
        update();
        break;
...
}
//里面没有处理QEvent::LanguageChange,需要我们自己实现
void QWidget::changeEvent(QEvent * event)
{
    switch(event->type()) {
    case QEvent::EnabledChange: {
        ...
        break;
    }
    case QEvent::FontChange:
    case QEvent::StyleChange: {
        ...
        break;
    }
    case QEvent::PaletteChange:
        update();
        break;
    case QEvent::ThemeChange:
        ...
        break;
    default:
        break;
    }
}

static bool removeTranslator(QTranslator * messageFile);

从此应用程序使用的翻译文件列表中删除翻译文件translationFile。
(它不会从文件系统中删除翻译文件。)
函数成功返回true,失败返回false。
也会触发QEvent::LanguageChange事件

static QString translate(const char * context,const char * key,const char * disambiguation = nullptr,int n = -1);

通过查询已安装的翻译文件,返回翻译文本。
context通常是一个类名(例如,“MyDialog”),
QObject::tr(“text123”)时context为"QObject",qApp->translate(“QObject”,“text123”);
QWidget::tr(“text123”)时context为"QWidget" ,qApp->translate(“QObject”,“text123”);
每个继承QObject并定义宏Q_OBJECT的类,元编译时都会自动生成静态的tr函数,
还可以看ts文件里面对应字符串节点下面的QObject节点的值就是context需要填写的值。
key就是翻译的原字符串,即代码里面tr(“str”),包含的字符串"str"
disambiguation是一个标识字符串,用于在相同上下文中以不同的角色使用相同的源文本。

动态切换多语言

语言切换就是先removeTranslator然后installTranslator新的翻译文件,触发重新翻译即可。
触发重新翻译:

  1. 使用UI编辑器来自动生成界面的情况,使用ui->retranslateUi()重新加载多语言。
  2. widget类型:重写virtual void changeEvent(QEvent *);
void MyWidget::changeEvent(QEvent * event)
{
    if(event->type() == QEvent::LanguageChange)
    {
        //重新获取翻译字符串
        qDebug()<<qApp->translate("MyWidget","obj1");
    }
    QWidget::changeEvent(event);
}
  1. 非widget类型:无法收到QEvent::LanguageChange事件(即重写changeEvent(QEvent * event函数也无效),此时就可以通过信号槽的方式实现,切换时触发信号,然后需要重新翻译的都用槽函数关联此信号即可

本机事件过滤

void installNativeEventFilter(QAbstractNativeEventFilter *filterObj);

为应用程序在主线程中接收的所有本机事件安装事件筛选器filterObj。
事件过滤器filterObj通过它的 nativeEventFilter()函数接收事件,
对于主线程中接收到的所有本机事件,都会调用该函数。
如果安装了多个事件筛选器,则首先激活最后安装的筛选器。
注意:这里设置的过滤函数接收本地消息,即MSG或XCB事件结构。
注意:当Qt::AA_PluginApplication属性被设置时,本机事件过滤器将在应用程序中被禁用。
由于其继承QObject所以其也有installEventFilter()函数。

void QCoreApplication::installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
{
    if (QCoreApplication::testAttribute(Qt::AA_PluginApplication)) {
        qWarning("Native event filters are not applied when the Qt::AA_PluginApplication attribute is set");
        return;
    }
    eventDispatcher->installNativeEventFilter(filterObj);
}

void QAbstractEventDispatcher::installNativeEventFilter(QAbstractNativeEventFilter *filterObj)
{
    Q_D(QAbstractEventDispatcher);

    // clean up unused items in the list
    d->eventFilters.removeAll(0);
    d->eventFilters.removeAll(filterObj);
    d->eventFilters.prepend(filterObj);
}

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) 
    {
        //不过滤本地消息则将消息转话成qt事件派发
        TranslateMessage(&msg);//将win或x11事件转换成QEvent事件
        DispatchMessage(&msg);//派发消息
    }
    activateEventNotifiers();
}

bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, void *message, long *result)
{
    Q_D(QAbstractEventDispatcher);
    if (!d->eventFilters.isEmpty()) {
        // Raise the loopLevel so that deleteLater() calls in or triggered
        // by event_filter() will be processed from the main event loop.
        QScopedScopeLevelCounter scopeLevelCounter(d->threadData);
        for (int i = 0; i < d->eventFilters.size(); ++i) {
            QAbstractNativeEventFilter *filter = d->eventFilters.at(i);
            if (!filter)
                continue;
            if (filter->nativeEventFilter(eventType, message, result))
                return true;
        }
    }
    return false;
}

void removeNativeEventFilter(QAbstractNativeEventFilter *filterObj);

移除本机事件过滤器

void QAbstractEventDispatcher::removeNativeEventFilter(QAbstractNativeEventFilter *filter)
{
    Q_D(QAbstractEventDispatcher);
    for (int i = 0; i < d->eventFilters.count(); ++i) {
        if (d->eventFilters.at(i) == filter) {
            d->eventFilters[i] = 0;
            break;
        }
    }
}

启动与退出

static int exec();

启动事件处理。
进入主事件循环并等待,直到exit()被调用。
返回传递给exit()的值(如果通过quit()调用exit(),该值为0)。
必须调用此函数来启动事件处理。

int QCoreApplication::exec()
{
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;

    QThreadData *threadData = self->d_func()->threadData;
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }

    threadData->quitNow = false;
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    int returnCode = eventLoop.exec();//开启事件循环
    threadData->quitNow = false;

    if (self)
        self->d_func()->execCleanup();

    return returnCode;
}

int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadRelaxed();

    if (threadData->quitNow)
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
    
    d->exit.storeRelease(false);//设置false
    
    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);
    //一直循环等待事件
    while (!d->exit.loadAcquire())
        processEvents(flags | WaitForMoreEvents | EventLoopExec);

    ref.exceptionCaught = false;
    return d->returnCode.loadRelaxed();
}

static void exit(int retcode=0);

调用此函数后,应用程序离开主事件循环并从调用exec()返回。
注意,与同名的C库函数不同,这个函数返回给调用者并事件处理停止。

void QCoreApplication::exit(int returnCode)
{
    if (!self)
        return;
    QThreadData *data = self->d_func()->threadData.loadRelaxed();
    data->quitNow = true;
    for (int i = 0; i < data->eventLoops.size(); ++i) {
        QEventLoop *eventLoop = data->eventLoops.at(i);
        eventLoop->exit(returnCode);
    }
}
void QEventLoop::exit(int returnCode)
{
    Q_D(QEventLoop);
    auto threadData = d->threadData.loadAcquire();
    if (!threadData->hasEventDispatcher())
        return;

    d->returnCode.storeRelaxed(returnCode);
    d->exit.storeRelease(true);//设置true
    threadData->eventDispatcher.loadRelaxed()->interrupt();
}

static void quit();

告诉应用程序退出并返回代码0(成功)。相当于调用QCoreApplication::exit(0)。

void QCoreApplication::quit()
{
    exit(0);
}

void aboutToQuit(QPrivateSignal);

此信号在应用程序即将退出主事件循环时发出
这可能发生在应用程序内部调用quit()之后,也可能发生在用户关闭整个桌面会话之后。
注意:这是一个私人信号。它可以用于信号连接,但不能由用户发出。
建议您将清理代码连接到aboutToQuit()信号,而不是将其放在应用程序的main()函数中,因为在某些平台上exec()调用可能不会返回。

void QCoreApplicationPrivate::execCleanup()
{
    threadData.loadRelaxed()->quitNow = false;
    in_exec = false;
    if (!aboutToQuitEmitted)
        emit q_func()->aboutToQuit(QCoreApplication::QPrivateSignal());
    aboutToQuitEmitted = true;
    QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
exec()退出时发送此信号
QApplication a(argc, argv);
QObject::connect(&a,&QApplication::aboutToQuit,[](){
       qDebug()<<"ewcv aboutToQuit signal " ;
});

event处理函数

bool QCoreApplication::event(QEvent *e)
{
    if (e->type() == QEvent::Quit) {
        quit();
        return true;
    }
    return QObject::event(e);
}
文章来源:https://blog.csdn.net/sinat_33263516/article/details/135685463
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。