QCad处理脚本的基本流程如下:
//将脚本的工厂函数和脚本类型注册到系统当中,【jsfactory--js, pytonfactory--python】
RScriptHandlerRegistry::registerScriptHandler(RScriptHandlerEcma::factory,
RScriptHandlerEcma::getSupportedFileExtensionsStatic());
//通过脚本工厂【jsfactory】创建具体的脚本类
RScriptHandler* handler = RScriptHandlerRegistry::getGlobalScriptHandler("js");
Q_ASSERT(handler!=NULL);
//初始化自动脚本程序【初始化过程中会创建主界面】
handler->init(autostartFile, arguments.mid(i+1));
//如果脚本初始化过程中出现异常,则返回
int ret = 0;
if (handler->hasUncaughtExceptions()) {
ret = 1;
}
// delete script handler and print uncaught exceptions:
//删除脚本句柄,打印异常
delete handler;
下面的代码用于在脚本解释器中初始化并启动脚本:
void RScriptHandler::init(const QString& autostartFile, const QStringList& arguments) {
QStringList triedLocations;
if (!autostartFile.isEmpty()) { //如果启动的脚本文件不是空的
QFileInfo fi(autostartFile);
//如果不是绝路径,就转换为绝对路径
if (!fi.isAbsolute() && !autostartFile.startsWith(":")) {
triedLocations << RSettings::getLaunchPath() + QDir::separator() + autostartFile;
}
triedLocations << autostartFile;
triedLocations << ":" + autostartFile;
}
else {
//如果没有设置文件则获取默认的脚本文件
QStringList extensions = getSupportedFileExtensions();
QStringList::iterator it;
for (it = extensions.begin(); it != extensions.end(); ++it) {
QString scriptFile = "scripts" + QString(QDir::separator()) + autostartScriptName + "." + (*it);
triedLocations << scriptFile;
triedLocations << ":" + scriptFile;
}
}
for (int i=0; i<triedLocations.size(); i++) {
if (QFileInfo(triedLocations[i]).exists()) {
//执行脚本
doScript(triedLocations[i], arguments);
return;
}
}
qWarning() << "Autostart script not found at: \n" << triedLocations.join("\\n");
}
下面的代码用于执行脚本的统一方法:
void RScriptHandlerEcma::doScript(const QString& scriptFile,const QStringList& arguments) {
QFileInfo fi(scriptFile);
if (!fi.exists()) {
//如果脚本不存在
qWarning()
<< QString("RScriptHandlerEcma::doScript: "
"file '%1' does not exist").arg(scriptFile);
return;
}
//如果这个脚本已经被加载过了就返回
if (isIncluded(engine, fi.completeBaseName())) {
return;
}
QScriptValue globalObject = engine->globalObject();
initGlobalVariables(scriptFile);
if (!arguments.isEmpty()) {
// set global variable args to (command line) arguments:
//设置args变量
globalObject.setProperty("args", qScriptValueFromValue(engine,arguments));
}
//读取脚本
QString contents = readScript(scriptFile, alwaysLoadScripts);
if (contents.isEmpty()) {
qDebug() << "RScriptHandlerEcma::doScript: script file is empty";
return;
}
//调用文件
eval(contents, scriptFile);
//将文件加入到解释器当中
markIncluded(engine, fi.completeBaseName());
}
已经加载的脚本文件列表保存在解释器中,下面是用于检测脚本是否已经加载的代码:
bool RScriptHandlerEcma::isIncluded(QScriptEngine* engine, const QString& className) {
if (alwaysLoadScripts && className!="library" && className!="EAction" && className!="WidgetFactory") {
//alwaysLoadScripts:同一个脚本可以重复加载
//"library"、"EAction"、"WidgetFactory"不能重复加载
// always include (again) to reload potential changes:
return false;
}
QVariant vAlreadyIncluded;
//判断解释器中是否有alreadyIncluded这个属性,alreadyIncluded是一个列表,包含已经加载的所有脚本
vAlreadyIncluded = engine->property("alreadyIncluded");
if (!vAlreadyIncluded.isValid()) {
//如果解释器中alreadyIncluded没有定义,则可以加载
return false;
}
//遍历脚本解释器中的vAlreadyIncluded列表看看是否存在className
QSet<QString> alreadyIncluded;
alreadyIncluded = vAlreadyIncluded.value<QSet<QString> >();
if (!alreadyIncluded.contains(className)) {
return false;
}
return true;
}
void RScriptHandlerEcma::initGlobalVariables(const QString& scriptFile) {
// initialize global ECMA variables:
//初始化全局变量,设置当前脚本以及当前脚本的全路径
QScriptValue globalObject = engine->globalObject();
globalObject.setProperty("scriptFile", QScriptValue(engine, scriptFile));
globalObject.setProperty("includeBasePath", QScriptValue(engine,
QFileInfo(scriptFile).absolutePath()));
}
当脚本加载完成后,将脚本标记为已经包含,代码如下:
void RScriptHandlerEcma::markIncluded(QScriptEngine* engine, const QString& className) {
QVariant vAlreadyIncluded;
QSet<QString> alreadyIncluded;
//获取已经加载的标记
vAlreadyIncluded = engine->property("alreadyIncluded");
if (vAlreadyIncluded.isValid()) {
//如果有效则创建一个QSet对象
alreadyIncluded = vAlreadyIncluded.value<QSet<QString> >();
}
//是否包含js文件,如果已经包含了就返回
if (alreadyIncluded.contains(className)) {
return;
}
//将类文件加入到变量当中
alreadyIncluded.insert(className);
vAlreadyIncluded.setValue(alreadyIncluded);
//设置到脚本解释器当中
engine->setProperty("alreadyIncluded", vAlreadyIncluded);
}