QCad源码分析 第五章

讲解一下Add-ons,即RAction的执行过程

前提条件是MainWindow已将所有的界面Add-ons加载完成,QCad中的Action都会绑定一个脚本,例如“Draw”菜单下的“Line”下的“Line2P”菜单项的执行过程。

首先Line2P在初始化时会绑定脚本,代码如下

//---------------------------Line2PInit.js文件-----------------------------------
function init(basePath) {
    //创建一个RGuiAction项,创建后此项会保存在C++类RGuiAction中的static成员列表中
    var action = new RGuiAction(qsTranslate("Line2P", "Line from &2 Points"), 
                                                RMainWindowQt.getMainWindow());
    action.setRequiresDocument(true);
    //设置RGuiAction项绑定的脚本
    action.setScriptFile(basePath + "/Line2P.js");
    //设置图标
    action.setIcon(basePath + "/Line2P.svg");
    //设置提示信息
    action.setStatusTip(qsTranslate("Line2P", "Draw single line or sequence of lines"));
    //设置快捷键
    action.setDefaultShortcut(new QKeySequence("l,i"));
    //设置命令字符,可以通过命令触发该action
    action.setDefaultCommands(["line", "ln", "li", "l"]);
    //设置分组
    action.setGroupSortOrder(6100);
    action.setSortOrder(100);
    //设置该action会出现在菜单、工具条、线工具面板、线矩阵面板中
    action.setWidgetNames(["DrawLineMenu", "LineToolBar", "LineToolsPanel", 
                                                                   "LineMatrixPanel"]);
}

其中action.setScriptFile(basePath + "/Line2P.js");显示此action绑定的脚本是“Line2P.js”

下面是相关的类结构图:

当“Line2P”菜单项被触发时,会调用“triggered”信号并调用相关的槽“slotTrigger”,信号槽的连接过程如下:

//RGuiAction类的构造函数
RGuiAction::RGuiAction(const QString& text, QObject* parent)
  : QAction(text, parent),
    factory(NULL),
    oriText(text),
    groupDefault(false),
    forceGlobal(false),
    requiresDocument(true),
    requiresSelection(false),
    requiresUndoableTransaction(false),
    requiresRedoableTransaction(false),
    override(false),
    allowInterrupt(false),
    noState(false),
    toggleable(false),
    iconDisabled(false),
    enabledOverride(-1) {
    //documentInterface(NULL) {
    
    ......
	//连接信号和槽
    connect(this, SIGNAL(triggered()), this, SLOT(slotTrigger()));

    ......
}

其中slotTrigger的处理过程如下:

bool RGuiAction::slotTrigger(const QString& command) {
	//获取主窗口
    RMainWindow* mainWindow = RMainWindow::getMainWindow();
	//
    if (mainWindow != NULL) {
        // display main command somewhere, e.g. in command line:
        if (command.isNull()) {
			//如果不是命令行调用,得到
            QString mainCommand = getMainCommand();
            if (!mainCommand.isEmpty()) {
				//设置命名为当前命令
                mainWindow->handleUserCommand(mainCommand);
            }
        }
        else {
			//设置命名为当前命令
            mainWindow->handleUserCommand(command);
        }
    }

    // uncheck all other actions in this group and check this action:
	//遍历action所在的组,将其他action的状态设置为uncheck
    if (!group.isEmpty()) {
        setChecked(true);
        QList<RGuiAction*> actions = actionsByGroup.values(group);
        for (int i = 0; i < actions.size(); ++i) {
            RGuiAction* action = actions.at(i);
            if (action!=this) {
				//设置其他action为uncheck
                action->setChecked(false);
            }
        }
    }

	//查看脚本文件是否存在
    if (scriptFile.size() > 0) {
        // call action factory of script handler:
        if (requiresDocument && !forceGlobal) {
            RDocumentInterface* di;
//            if (documentInterface!=NULL) {
//                di = documentInterface;
//                //qDebug() << "got di: " << (unsigned long int)di;
//            }
//            else {
				//获取当前文档
                di = RMainWindow::getDocumentInterfaceStatic();
                //qDebug() << "getting di statically: " << (unsigned long int)di;
//            }
            if (di == NULL) {
                qWarning() << "This action requires a document to be open: " << scriptFile;
                return true;
            }
			//如果当前action已经触发,则终止这次的action
            if (isToggleable() && !isChecked()) {
                // if action is toggleable, terminate already running action:
                di->terminateCurrentAction();
                return true;
            }

			//获取脚本后缀
            QString extension = QFileInfo(scriptFile).suffix();
			//根据扩展得到脚本处理类
            RScriptHandler* scriptHandler = di->getScriptHandler(extension);
            if (scriptHandler == NULL) {
                qWarning("RGuiAction::slotTrigger: "
                    "no script handler found for scriptFile: %s",
                        (const char*) scriptFile.toUtf8());
                return false;
            }
			//调用脚本
            scriptHandler->createActionDocumentLevel(scriptFile, this);
        } else {
            RScriptHandler::triggerActionApplicationLevel(scriptFile, this);
        }
        emit postTriggered();
        return true;
    } else if (factory != NULL) {
        // call C++ action factory:
        factory(this);
        emit postTriggered();
        return true;
    }

    //qWarning("RGuiAction::doAction: factory is NULL");
    // A QAction based action might choose to call QAction::trigger if false is
    // returned:
    emit postTriggered();
    return false;
}

其中的 scriptHandler->createActionDocumentLevel(scriptFile, this); 代码段会调用该RGuiAction绑定的“Line2P.js”文件,createActionDocumentLevel的处理过程如下:

void RScriptHandlerEcma::createActionDocumentLevel(const QString &scriptFile,
        RGuiAction *guiAction, RDocumentInterface *documentInterface)
{

    if (engine->isEvaluating())
    {
        //        qWarning() << "RScriptHandlerEcma::createActionDocumentLevel(): "
        //                   << scriptFile
        //                   << ": Engine is busy. Aborting...";
        if (guiAction == NULL || !guiAction->getAllowInterrupt())
        {
            return;
        }
    }

    if (guiAction == NULL)
    {
        qWarning() << "RScriptHandlerEcma::createActionDocumentLevel(): "
                   << "guiAction is NULL";
    }

    //    if (documentInterface == NULL && guiAction!=NULL) {
    //        documentInterface = guiAction->getDocumentInterface();
    //    }
    if (documentInterface == NULL)
    {
        documentInterface = RMainWindow::getDocumentInterfaceStatic();
    }
    if (documentInterface == NULL)
    {
        qWarning() << "RScriptHandlerEcma::createActionDocumentLevel(): "
                   << scriptFile
                   << ": No document interface given or found.";
        return;
    }

    if (!QFileInfo(scriptFile).exists())
    {
        getScriptEngine().currentContext()->throwError(QString(
                    "File %1 does not exists.").arg(scriptFile));
        return;
    }

    //不在用的脚本内存就收集起来
    engine->collectGarbage();

    //执行脚本
    doScript(scriptFile);

    //获取文件名的基本名称
    QString className = QFileInfo(scriptFile).completeBaseName();
    //获取全局变量
    QScriptValue globalObject = getScriptEngine().globalObject();

    //将action导出为脚本对象
    globalObject.setProperty("guiAction", engine->toScriptValue(guiAction));
    //将documentinterface导出为脚本对象
    globalObject.setProperty("documentInterface", engine->toScriptValue(documentInterface));
    //将创建的对象加入到documentInterface中【例如new Line();】
    eval("documentInterface.setCurrentAction(new " + className + "(guiAction));");

    /*
    QScriptValue ecmaConstructor = globalObject.property(className);
    if (!(ecmaConstructor.isValid() && ecmaConstructor.isFunction())) {
        getScriptEngine().currentContext()->throwError(
                QString("Class not found or not valid: %1").arg(className));
        return;
    }

    // call constructor of ECMA class with GUI action as argument:
    QScriptValueList constructorArgs;
    constructorArgs.append(getScriptEngine().toScriptValue(guiAction));
    QScriptValue ecmaObject = ecmaConstructor.construct(constructorArgs);

    //    if (engine->hasUncaughtException()) {
    //        qWarning() << engine->uncaughtException().toString();
    //        qWarning() << "Exception Backtrace:";
    //        qWarning() << engine->uncaughtExceptionBacktrace().join("\n");
    //        Q_ASSERT(false);
    //    }

    if (!ecmaObject.isValid()) {
        getScriptEngine().currentContext()->throwError(QString(
                "Constructor %1 not found.").arg(className));
        return;
    }

    REcmaShellActionAdapter* action = qscriptvalue_cast<REcmaShellActionAdapter*>(ecmaObject.prototype());
    if (action!=NULL) {
        if (documentInterface != NULL) {
            // essential to rescue member variables that were set in the constructor:
            action->__qtscript_self = ecmaObject;
            documentInterface->setCurrentAction(action);
        }
        else {
            qDebug() << "RScriptHandlerEcma::createActionDocumentLevel: documentInterface is NULL";
        }
    }
    else {
        qDebug() << "RScriptHandlerEcma::createActionDocumentLevel: action is NULL";
    }
    */
}

其中的doScript(scriptFile);将会把“Line2P.js”加入到QScriptEngine当中,然后会在eval("documentInterface.setCurrentAction(new " + className + "(guiAction));");一句中拼接出“new Line2P('guiaction')”.

//将guiaction导出为脚本对象
    globalObject.setProperty("guiAction", engine->toScriptValue(guiAction));
    //将documentinterface导出为脚本对象
    globalObject.setProperty("documentInterface", engine->toScriptValue(documentInterface));
    //将创建的对象加入到documentInterface中【例如new Line();】
    eval("documentInterface.setCurrentAction(new " + className + "(guiAction));");

创建Line2P对象以后,鼠标的拾取点的操作都是在操作Line2P对象。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: QCAD源码编译过程中没有生成qcad.exe的原因可能有以下几种: 1. 编译过程中出现了错误或警告:在编译源码的过程中,可能会出现一些错误或警告信息,这些问题可能会导致生成的可执行文件不存在。需要检查编译过程中的日志信息,查找并解决出现的错误。 2. 编译配置问题:在进行源码编译时,可能需要进行一些配置操作,例如指定生成的可执行文件的名称、路径等。如果配置不正确,可能导致生成的可执行文件不存在。需要仔细检查编译配置,确保生成的可执行文件的相关设置正确无误。 3. 缺少依赖项:在编译源码时,可能需要依赖一些第三方库或工具。如果缺少了这些依赖项,可能会导致生成的可执行文件不存在。需要查看编译过程中的依赖项信息,确保所有依赖项都已正确安装并配置。 4. 操作系统兼容性问题:有些情况下,源码可能只能在特定的操作系统上编译成功。如果您的操作系统与源码不兼容,可能无法生成可执行文件。需要确认您的操作系统是否符合源码的兼容要求,如果不符合,可能需要考虑更换操作系统或寻找适应您当前环境的其他解决方案。 综上所述,QCAD源码编译没有生成qcad.exe可能是由于编译过程中的错误、配置问题、缺少依赖项或操作系统兼容性等原因所致。您可以详细检查编译过程中的日志信息、编译配置、依赖项安装以及操作系统兼容性,以解决生成qcad.exe的问题。 ### 回答2: QCAD是一个开源的计算机辅助设计(CAD)软件,使用C++语言编写。为了编译QCAD源码并生成可执行文件(qcad.exe),您需要按照以下步骤进行操作: 1. 首先,确保您的计算机已经安装了适当的开发环境,如GCC编译器、CMake和Qt开发工具包。 2. 下载QCAD源码压缩包,可以从QCAD的官方网站或者GitHub仓库获取。 3. 解压源码压缩包到您选择的目录中。 4. 打开命令行终端,并导航到源码目录。 5. 创建一个新的文件夹,用于存储生成的可执行文件。 6. 在命令行中输入以下命令来配置编译过程: ```shell cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=/path/to/qcad_executable_directory . ``` 这将使用Unix Makefiles生成器配置构建过程,并指定编译后的文件存储路径。 7. 一旦配置完成,使用以下命令进行编译: ```shell make ``` 这将开始编译源码,并生成可执行文件。编译时间可能会有所不同,具体取决于您的计算机性能和源码规模。 8. 编译完成后,在指定的保存路径下应该会生成一个名为qcad.exe的可执行文件。 如果在编译过程中出现错误或警告,请检查您的环境设置和依赖项,可能需要解决一些缺失的库或依赖关系。同时,确保按照官方文档提供的编译指南进行操作。 ### 回答3: QCAD源码编译生成的可执行文件qcad.exe可能没有成功生成的原因有以下几点: 1. 编译过程中出现错误:在编译源码的过程中,可能会因为各种原因导致编译错误,比如缺少依赖库、编译选项设置错误等。这些错误会中断编译过程,导致最终的可执行文件没有生成。需要仔细检查编译日志或错误信息,解决相关编译错误。 2. 缺少构建环境:在编译QCAD源码之前,需要确定是否已经安装了编译所需的构建环境和工具链。这包括编译器、构建系统(如CMake)、相关依赖库等。如果缺少必要的构建环境,编译过程将无法进行或无法正确完成,因此不会生成可执行文件。 3. 编译选项未正确设置:编译过程中需要设置相应的编译选项,以配置编译器、链接选项和依赖库等。如果编译选项设置不正确,可能导致编译过程中的错误或生成的可执行文件出错。需要确认编译选项的设置是否正确。 4. 缺少构建目标:在进行源码编译时,需要指定正确的编译目标。如果没有正确指定目标,编译系统将无法找到生成可执行文件的相关源码文件,并最终没有生成qcad.exe。 针对这些可能的原因,我们需要逐个排查和解决。通过仔细检查编译日志、确认构建环境和工具链的安装情况、修正编译选项以及检查是否正确指定了构建目标,可以解决未生成qcad.exe的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值