CTK-使用ctk框架完成日志、打印、界面插件
一、项目介绍
1、主要为了在此小型测试项目中用到几乎所有ctk的常用知识:
插件间通信、服务工厂、服务追踪、事件监听、框架封装
2、项目共包含3个插件
日志插件:用于模拟将接收的信息存储到日志中【这里只做简单的打印】,并且能够接收信息【主要来自界面插件】
打印插件:根据不同的插件返回提供不同打印服务
界面插件:封装一个界面插件,能够向框架发送信息【类方式和信号槽方式】
3、架构图
二、打印插件编写
1、工程结构
2、接口类
absprintserver.h
#ifndef ABSPRINTSERVER_H #define ABSPRINTSERVER_H #include <QObject> class AbsPrintServer { public: virtual ~AbsPrintServer(){} virtual void print(QString) = 0; }; Q_DECLARE_INTERFACE(AbsPrintServer,"judesmorning.zxy.AbsPrintServer") #endif // ABSPRINTSERVER_H
3、实现类1,为日志插件提供服务
printserver.h
#ifndef PRINTSERVER_H #define PRINTSERVER_H #include <QObject> #include "absprintserver.h" class PrintServer : public QObject, public AbsPrintServer { Q_OBJECT Q_INTERFACES(AbsPrintServer) public: PrintServer(); void print(QString info) override; }; #endif // PRINTSERVER_H
printserver.cpp
#include "printserver.h" #include <QDebug> PrintServer::PrintServer() { } void PrintServer::print(QString info) { qDebug()<<info+"----print plugin for FirstPlugin"; }
4、实现类2,为界面插件提供服务
printservert.h
#ifndef PRINTSERVERT_H #define PRINTSERVERT_H #include <QObject> #include "absprintserver.h" class PrintServerT : public QObject, public AbsPrintServer { Q_OBJECT Q_INTERFACES(AbsPrintServer) public: PrintServerT(); void print(QString info) override; }; #endif // PRINTSERVERT_H
printservert.cpp
#include "printservert.h" #include <QDebug> #include <QTime> PrintServerT::PrintServerT() { } void PrintServerT::print(QString info) { qDebug()<<QTime::currentTime().toString("HH:mm:ss ")<<info+"----print plugin for CreateCtkUiPlugin"; }
5、实现类3,为其他插件提供服务
printserverd.h
#ifndef PRINTSERVERD_H #define PRINTSERVERD_H #include <QObject> #include "absprintserver.h" class PrintServerD : public QObject, public AbsPrintServer { Q_OBJECT Q_INTERFACES(AbsPrintServer) public: PrintServerD(); void print(QString info) override; }; #endif // PRINTSERVERD_H
printserverd.cpp
#include "printserverd.h" #include <QDebug> PrintServerD::PrintServerD() { } void PrintServerD::print(QString info) { qDebug()<<info+"....----print plugin for unknown plugin";; }
6、单独为接口编写服务追踪类
tracker.h
#ifndef TRACKER_H #define TRACKER_H #include "ctkServiceTracker.h" #include "absprintserver.h" #include "ctkPluginContext.h" class Tracker : public ctkServiceTracker<AbsPrintServer*> { public: Tracker(ctkPluginContext* context); protected: AbsPrintServer* addingService(const ctkServiceReference& reference) override; void modifiedService(const ctkServiceReference& reference, AbsPrintServer* service) override; void removedService(const ctkServiceReference& reference, AbsPrintServer* service) override; private: ctkPluginContext* context; }; #endif // TRACKER_H
tracker.cpp
#include "tracker.h" Tracker::Tracker(ctkPluginContext *context) : ctkServiceTracker<AbsPrintServer*> (context) { } AbsPrintServer *Tracker::addingService(const ctkServiceReference &reference) { AbsPrintServer* service = static_cast<AbsPrintServer*>(ctkServiceTracker::addingService(reference)); return service; } void Tracker::modifiedService(const ctkServiceReference &reference, AbsPrintServer* service) { ctkServiceTracker::modifiedService(reference,service); } void Tracker::removedService(const ctkServiceReference &reference, AbsPrintServer* service) { ctkServiceTracker::removedService(reference,service); }
注意这个服务追踪类是在日志插件里使用的,只是从职责上来讲这个类应该编写此插件的人来编写,降低耦合性
三、日志插件编写
1、工程结构
2、接口类
abslogservice.h
#ifndef ABSLOGSERVICE_H #define ABSLOGSERVICE_H #include <QObject> class AbsLogService{ public: virtual ~AbsLogService(){} virtual void log(QString info) = 0; }; Q_DECLARE_INTERFACE(AbsLogService,"judesmorning.zxy.AbsLogService") #endif // ABSLOGSERVICE_H
3、实现类
logservice.h
#ifndef LOGSERVICE_H #define LOGSERVICE_H #include <QObject> #include "includes.h" #include "abslogservice.h" #include "service/event/ctkEventHandler.h" #include "ctkPluginFrameworkEvent.h" #include "ctkPluginEvent.h" #include "ctkServiceEvent.h" #include "tracker.h" class ctkPluginContext; class LogService : public QObject, public AbsLogService, public ctkEventHandler { Q_OBJECT Q_INTERFACES(AbsLogService) Q_INTERFACES(ctkEventHandler) public: LogService(ctkPluginContext* context); void log(QString info) override; void handleEvent(const ctkEvent& event) override; private slots: // 监听框架事件 void onFrameworkEvent(const ctkPluginFrameworkEvent& event); // 监听插件事件 void onPluginEvent(const ctkPluginEvent& event); // 监听服务事件 void onServiceEvent(const ctkServiceEvent& event); private: QString getLevelStr(const _Log_Level& level) const;//获取日志类型字符串 void print(QString info);//使用打印插件打印东西 private: ctkPluginContext* context; QScopedPointer<Tracker> p_tracker; }; #endif // LOGSERVICE_H
logservice.cpp
#include "logservice.h" #include <QTime> #include <QDebug> #include "ctkPluginContext.h" #include "absprintserver.h" #include "ctkServiceTracker.h" LogService::LogService(ctkPluginContext* context) :context(context) { #if 1 //通过服务追踪方式获取打印插件 p_tracker.reset(new Tracker(context)); p_tracker->open(); // AbsPrintServer* printService = static_cast<AbsPrintServer*>(p_tracker->getService()); // printService->print("use tracker to get print plugin--------log plugin"); #endif #if 1 //监听ctk事件 context->connectFrameworkListener(this, SLOT(onFrameworkEvent(ctkPluginFrameworkEvent))); context->connectPluginListener(this, SLOT(onPluginEvent(ctkPluginEvent))); //QString filter = QString("(%1=%2)").arg(ctkPluginConstants::OBJECTCLASS).arg("org.commontk.eventadmin");// 过滤 ctkEventAdmin 服务 //context->connectServiceListener(this,SLOT(onServiceEvent(ctkServiceEvent))); //, filter); context->connectServiceListener(this,"onServiceEvent"); //, filter); #endif } void LogService::log(QString info) { qDebug()<<"log plugin save a log--->"+QTime::currentTime().toString("HH:mm:ss ") + info; } void LogService::handleEvent(const ctkEvent &event) { qDebug()<<"log plugin rcv a event----------log plugin"; _Log_Level level = static_cast<_Log_Level>(event.getProperty("level").toInt()); QString pluginName = event.getProperty("pluginName").toString(); QString info = event.getProperty("info").toString(); QString rcvLogInfo = QString("%1 %2 %3.") .arg(getLevelStr(level)) .arg(pluginName) .arg(info); log(rcvLogInfo); } QString LogService::getLevelStr(const _Log_Level &level) const { QString ret; switch (static_cast<int>(level)) { case LOG_LEVEL_DEBUG: ret = "DEBUG"; break; case LOG_LEVEL_INFO: ret = "INFO"; break; case LOG_LEVEL_WARNING: ret = "WARNING"; break; case LOG_LEVEL_ERROR: ret = "ERROR"; break; case LOG_LEVEL_CRITICAL: ret = "CRITICAL"; break; default: ret = "UKNOWN"; break; } return ret; } void LogService::print(QString info) { #if 0 //直接向ctk框架索要服务 ctkServiceReference reference = context->getServiceReference<AbsPrintServer>(); if (reference) { // 获取指定 ctkServiceReference 引用的服务对象 AbsPrintServer* service = qobject_cast<AbsPrintServer*>(context->getService(reference)); if (service != Q_NULLPTR) { // 调用服务 service->print(info); } } #endif #if 1 //通过tracker向ctk索要服务 AbsPrintServer* service = static_cast<AbsPrintServer*>(p_tracker->getService()); if (service != Q_NULLPTR) { service->print("log plugin with tracker:"+info); } else { qDebug()<<"get AbsPrintServer from tracker failed"; } #endif } void LogService::onFrameworkEvent(const ctkPluginFrameworkEvent &event) { if (!event.isNull()) { QSharedPointer<ctkPlugin> plugin = event.getPlugin(); qDebug() << "FrameworkEvent: [" << plugin->getSymbolicName() << "]" << event.getType() << event.getErrorString(); } else { qDebug() << "The framework event is null"; } } void LogService::onPluginEvent(const ctkPluginEvent &event) { if (!event.isNull()) { QSharedPointer<ctkPlugin> plugin = event.getPlugin(); qDebug() << "PluginEvent: [" << plugin->getSymbolicName() << "]" << event.getType(); } else { qDebug() << "The plugin event is null"; } } void LogService::onServiceEvent(const ctkServiceEvent &event) { if (!event.isNull()) { ctkServiceReference ref = event.getServiceReference(); QSharedPointer<ctkPlugin> plugin = ref.getPlugin(); qDebug() << "ServiceEvent: [" << plugin->getSymbolicName() << "]" << event.getType() << ref.getUsingPlugins(); } else { qDebug() << "The service event is null"; } }
4、激活类
firstpluginactivator.h
#ifndef FIRSTPLUGINACTIVATOR_H #define FIRSTPLUGINACTIVATOR_H #include <QObject> #include "ctkPluginActivator.h" #include "ctkPluginContext.h" #include "logservice.h" class FirstPluginActivator : public QObject, public ctkPluginActivator { Q_OBJECT Q_INTERFACES(ctkPluginActivator) Q_PLUGIN_METADATA(IID "LogPlugin") public: FirstPluginActivator(); void start(ctkPluginContext *context); void stop(ctkPluginContext *context); private: QScopedPointer<AbsLogService> m_log; }; #endif // FIRSTPLUGINACTIVATOR_H
firstpluginactivator.cpp
#include "firstpluginactivator.h" #include <QDebug> #include "service/event/ctkEventHandler.h" #include "service/event/ctkEventConstants.h" #include "absprintserver.h" FirstPluginActivator::FirstPluginActivator() { } //注册插件并订阅事件 void FirstPluginActivator::start(ctkPluginContext *context) { // qDebug() << "FirstPluginActivator start"; LogService* service = new LogService(context); m_log.reset(service); ctkDictionary props; props[ctkEventConstants::EVENT_TOPIC] = "kdhy/yunwei/generalevent/log"; props[ctkEventConstants::EVENT_FILTER] = "(pluginName=CreateCtkUiPlugin)"; context->registerService<ctkEventHandler>(service, props); context->registerService<AbsLogService>(service); } void FirstPluginActivator::stop(ctkPluginContext *context) { Q_UNUSED(context) }
5、插件间通信的结构体定义
includes.h
#ifndef INCLUDES_H #define INCLUDES_H #include <QString> /*公共定义*/ //日志相关 enum _Log_Level{ LOG_LEVEL_DEBUG = 0, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR, LOG_LEVEL_CRITICAL };//日志等级 typedef struct _LogInfo { _Log_Level level; //日志类型 QString pluginName; //插件名称 QString info; //日志信息 } LogInfo;//日志详情 #endif // INCLUDES_H
四、界面类
1、工程结构
2、接口类
absuiservice.h
#ifndef ABSUISERVICE_H #define ABSUISERVICE_H #include <QObject> class AbsUiService{ public: virtual ~AbsUiService(){} virtual void init() = 0; }; Q_DECLARE_INTERFACE(AbsUiService,"judesmorning.zxy.AbsUiService") #endif // ABSUISERVICE_H
3、实现类
uiservice.h
#ifndef UISERVICE_H #define UISERVICE_H #include <QObject> #include "absuiservice.h" #include "includes.h" #include "myui.h" class ctkPluginContext; class UiService : public QObject, public AbsUiService { Q_OBJECT Q_INTERFACES(AbsUiService) public: UiService(ctkPluginContext* context); void init() override; private slots: void publishLogEventSlot(LogInfo logInfo); private: ctkPluginContext* context;//ctk上下文对象 Myui myui;//界面对象 }; #endif // UISERVICE_H
uiservice.cpp
#include "uiservice.h" #include "ctkPluginContext.h" #include "service/event/ctkEventAdmin.h" #include <QDebug> #include <QDialog> UiService::UiService(ctkPluginContext* context) :context(context) { context->registerService<AbsUiService>(this); QObject::connect(&myui,SIGNAL(publishLogEventSignal(LogInfo)),this,SLOT(publishLogEventSlot(LogInfo))); //发送日志事件到ctk框架,signal方式 ctkServiceReference ref = context->getServiceReference<ctkEventAdmin>(); if (ref) { ctkEventAdmin* eventAdmin = context->getService<ctkEventAdmin>(ref); eventAdmin->publishSignal(&myui,SIGNAL(publishLogSignal(ctkDictionary)),"kdhy/yunwei/generalevent/log",Qt::QueuedConnection); } } void UiService::init() { myui.show(); } //发送日志事件到ctk框架,event方式 void UiService::publishLogEventSlot(LogInfo logInfo) { ctkServiceReference ref = context->getServiceReference<ctkEventAdmin>(); if (ref) { ctkEventAdmin* eventAdmin = context->getService<ctkEventAdmin>(ref); ctkDictionary props; props["level"] = logInfo.level; props["pluginName"] = logInfo.pluginName; props["info"] = logInfo.info; ctkEvent event("kdhy/yunwei/generalevent/log", props); qDebug() << "ui plugin send " << logInfo.info; eventAdmin->sendEvent(event);//sendEvent:同步 postEvent:异步 } }
4、主显示界面
myui.h
#ifndef MYUI_H #define MYUI_H #include <QWidget> #include "includes.h" #include "service/event/ctkEventAdmin.h" namespace Ui { class Myui; } class Myui : public QWidget { Q_OBJECT public: explicit Myui(QWidget *parent = nullptr); ~Myui(); signals: void publishLogEventSignal(LogInfo); void publishLogSignal(ctkDictionary info); private slots: void on_pushButton_clicked(); void on_pushButton_2_clicked(); private: Ui::Myui *ui; }; #endif // MYUI_H
myui.cpp
#include "myui.h" #include "ui_myui.h" Myui::Myui(QWidget *parent) : QWidget(parent), ui(new Ui::Myui) { ui->setupUi(this); } Myui::~Myui() { delete ui; } //发送event事件 void Myui::on_pushButton_clicked() { qDebug()<<"ui plugin send a event"; LogInfo logInfo; logInfo.level = LOG_LEVEL_INFO; logInfo.pluginName = "CreateCtkUiPlugin"; logInfo.info = "Event info:"+ui->textEdit->toPlainText(); emit publishLogEventSignal(logInfo); } //发送信号事件 void Myui::on_pushButton_2_clicked() { ctkDictionary props; props["level"] = LOG_LEVEL_INFO;; props["pluginName"] = "CreateCtkUiPlugin"; props["info"] = "Signal info:"+ui->textEdit->toPlainText(); emit publishLogSignal(props); }
myui.ui
5、激活类
uiactivator.h
#ifndef UIACTIVATOR_H #define UIACTIVATOR_H #include <QObject> #include "ctkPluginActivator.h" #include "ctkPluginContext.h" #include "uiservice.h" class UiActivator: public QObject, public ctkPluginActivator { Q_OBJECT Q_INTERFACES(ctkPluginActivator) Q_PLUGIN_METADATA(IID "UiPlugin") public: UiActivator(); void start(ctkPluginContext *context); void stop(ctkPluginContext *context); private: QScopedPointer<AbsUiService> m_ui; }; #endif // UIACTIVATOR_H
uiactivator.cpp
#include "uiactivator.h" #include <QtDebug> UiActivator::UiActivator() { } void UiActivator::start(ctkPluginContext *context) { // qDebug() << "ui plugin start"; m_ui.reset(new UiService(context)); } void UiActivator::stop(ctkPluginContext *context) { Q_UNUSED(context) }
6、插件间通信的结构体定义
includes.h
#ifndef INCLUDES_H #define INCLUDES_H #include <QString> /*公共定义*/ //日志相关 enum _Log_Level{ LOG_LEVEL_DEBUG = 0, LOG_LEVEL_INFO, LOG_LEVEL_WARNING, LOG_LEVEL_ERROR, LOG_LEVEL_CRITICAL };//日志等级 typedef struct _LogInfo { _Log_Level level; //日志类型 QString pluginName; //插件名称 QString info; //日志信息 } LogInfo;//日志详情 #endif // INCLUDES_H
五、使用
1、新建控制台工程
由于用到了界面,所以这个工程不能是无界面的
2、.pro
#------------------------------------------------- # # Project created by QtCreator 2020-07-02T18:12:37 # #------------------------------------------------- #QT += core QT += gui widgets CONFIG += console c++11 CONFIG -= app_bundle TARGET = CtkFramework #TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which has been marked as deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ pullservice.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target # CTK源码路径 INCLUDEPATH += $$PWD/third_libs/ctk/include/CTK_src/Core \ += $$PWD/third_libs/ctk/include/CTK_src/PluginFramework # CTK安装路径 INCLUDEPATH += $$PWD/third_libs/ctk/include/CTK_install/Core \ += $$PWD/third_libs/ctk/include/CTK_install/PluginFramework # CTK库路径 LIBS += -L$$PWD/third_libs/ctk/libs -lCTKCore -lCTKPluginFramework # 插件头文件路径 INCLUDEPATH += $$PWD/third_libs/plugin/include HEADERS += \ pullservice.h
3、框架封装类
pullservice.h
#ifndef PULLSERVICE_H #define PULLSERVICE_H #include <QDebug> #include <mutex> #include <QDir> #include <QTime> #include "ctkPluginFrameworkLauncher.h" #include "ctkPluginContext.h" #include "ctkPluginException.h" #include "ctkPluginFrameworkFactory.h" #include "ctkPluginFramework.h" #include "ctkPluginException.h" #include "ctkPluginContext.h" namespace PULLSERVICE{ #define PRINTF_LOCATION() qDebug()<<"ret in:" << __FILE__ << " at:"<<__LINE__ #define RET_VALUE_IF_NOT_EAQU(a,b,c) \ do { \ if(a!=b) \ { \ PRINTF_LOCATION();\ return c; \ } \ } while (false) #define RET_VALUE_IF_EAQU(a,b,c) \ do { \ if(a==b) \ { \ PRINTF_LOCATION();\ return c; \ } \ } while (false) #define RET_IF_NOT_EAQU(a,b) \ do { \ if(a!=b) \ { \ PRINTF_LOCATION();\ return; \ } \ } while (false) #define RET_IF_EAQU(a,b) \ do { \ if(a==b) \ { \ PRINTF_LOCATION();\ return; \ } \ } while (false) } using namespace PULLSERVICE; class AbsUiService; class AbsLogService; class PullService { protected: virtual ~PullService(); private: PullService(); // PullService(const PullService&) = delete; // PullService& operator=(const PullService&) = delete; void print(QString info); public: static PullService* getInstance(); //对外接口 void initCtkFramework(bool usingEventAdmin = false);//初始化框架 void initServices();//初始化所有服务 template<class T>//获取服务,通过模板 T* getService() { T* s = nullptr; RET_VALUE_IF_EAQU(context,nullptr,s); ctkServiceReference reference = context->getServiceReference<T>(); if(reference) { s = context->getService<T>(reference);// 获取指定 ctkServiceReference 引用的服务对象 if (s == nullptr) { print("Try to get a invalid service"); } } return s; } void stopFramework();//关闭ctk框架 private: static PullService* instance; static std::mutex mMutex; volatile bool usingEventAdmin = false; private: QStringList pluginNames;//所有插件名字 //ctk相关的变量 ctkPluginFrameworkFactory frameworkFactory; ctkPluginContext* context = nullptr; private: Q_DISABLE_COPY(PullService) }; #endif // PULLSERVICE_H
pullservice.cpp
#include "pullservice.h" //插件头文件 #include "abslogservice.h" #include "absuiservice.h" #include "signal.h" #include "slot.h" #include "absprintserver.h" #include "abslogtracker.h" PullService* PullService::instance = nullptr; std::mutex PullService::mMutex; PullService::PullService() { print("PullService construct"); pluginNames << "MPrintServer.dll" << "ctk-plugin-first.dll" << "CreateCtkUiPlugin.dll" // << "ctksignalplugin.dll" // << "ctkslotplugin.dll" << "ctkplugintracker.dll" ; } PullService::~PullService() { print("PullService destruct"); } PullService* PullService::getInstance() { if(nullptr == instance) { mMutex.lock(); if(nullptr == instance) { instance = new PullService; } mMutex.unlock(); } return instance; } /*****************************************************************/ //作者:朱小勇 //函数名称:初始化ctk框架 //函数参数:usingEventAdmin是否使用eventadmin //函数返回值:NULL //函数作用:NULL //备注:NULL /*****************************************************************/ void PullService::initCtkFramework(bool usingEventAdmin) { if(nullptr != context) { print("ctkPluginContext is not null at first time,maybe you have call this method.Try restart app to resolve this problem"); return; } this->usingEventAdmin = usingEventAdmin; if(usingEventAdmin) { QString path = QDir::currentPath() + "/third_libs/ctk/libs"; // 获取插件所在位置 ctkPluginFrameworkLauncher::addSearchPath(path); // 在插件的搜索路径列表中添加一条路径 ctkPluginFrameworkLauncher::start("org.commontk.eventadmin"); context = ctkPluginFrameworkLauncher::getPluginContext(); } else { QSharedPointer<ctkPluginFramework> framework = frameworkFactory.getFramework(); // 初始化并启动插件框架 try { framework->init(); framework->start(); context = framework->getPluginContext(); } catch (const ctkPluginException &e) { this->print("CTK plugin framework init failed:"+QString(e.what())); } } } /*****************************************************************/ //作者:朱小勇 //函数名称:初始化服务 //函数参数:NULL //函数返回值:NULL //函数作用:NULL //备注:安装启动插件 /*****************************************************************/ void PullService::initServices() { RET_IF_EAQU(context,nullptr); QString prefixFilePath = QDir::currentPath()+"/third_libs/plugin/libs/"; QString path; foreach (QString oneFileName , pluginNames) { path = prefixFilePath+oneFileName; try { // 安装插件 print(QString("Ready to init plugin:%1").arg(oneFileName)); QSharedPointer<ctkPlugin> plugin = context->installPlugin(QUrl::fromLocalFile(path)); print(QString("Plugin[%1_%2] installed...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString())); // 启动插件 plugin->start(ctkPlugin::START_TRANSIENT); print(QString("Plugin[%1_%2] started...").arg(plugin->getSymbolicName()).arg(plugin->getVersion().toString())); } catch (const ctkPluginException &e) { print(QString("Failed install or start plugin:%1").arg(e.what())); } } } /*****************************************************************/ //作者:朱小勇 //函数名称:停止框架 //函数参数:NULL //函数返回值:NULL //函数作用:NULL //备注:NULL /*****************************************************************/ void PullService::stopFramework() { if(usingEventAdmin) { ctkPluginFrameworkLauncher::stop(); } else { QSharedPointer<ctkPluginFramework> framework = frameworkFactory.getFramework(); framework->stop(); } } /*****************************************************************/ //作者:朱小勇 //函数名称:内部打印函数 //函数参数:NULL //函数返回值:NULL //函数作用:NULL //备注:NULL /*****************************************************************/ void PullService::print(QString info) { QString _i = QString("%1 %2 %3") .arg(__FILE__) .arg(QTime::currentTime().toString("HH:mm:ss")) .arg(info); qDebug()<<_i; }
3、main.cpp
#include <QApplication>//本地头文件 #include "pullservice.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); (void)PullService::getInstance()->initCtkFramework(false); (void)PullService::getInstance()->initServices(); return a.exec(); }
4、运行结果
长风破浪会有时,直挂云帆济沧海!
可通过下方链接找到博主
https://www.cnblogs.com/judes/p/10875138.html