QCAD Plugin 开发

QCAD Plugin 开发

[email protected]

Abstract. QCAD是基于GPL协议的开源CAD软件,核心功能基于Qt使用C++开发,界面及其交互使用Javascript脚本进行开发。QCAD官方推荐开发其Plugin的方式为使用Javascript脚本的方式,因为QCAD的菜单及其对应的功能全部由Javascript实现。程序有时也需要和C++直接通信,如在QCAD中使用OpenCASCADE。本文主要介绍如何来开发QCAD的插件Plugin,从而能够对QCAD进行扩展,做一些定制化的功能开发。

Key Words. QCAD Plugin, Javascript, C++, CAD, 3D

1.Introduction

QCAD是GPL协议的开源CAD软件,主要使用Javascript脚本进行开发,也可使用C++开发。与AutoCAD的多种开发方式一样,支持AutoLisp脚本,也支持ObjectArx使用C++进行开发。不过开源的程序可以进行源码Debug,遇到问题可以自己动手解决。而AutoCAD是闭源的,如果是正版用户可以咨询开发厂家,不能追根溯源。对于想学习CAD的人来说,建议可以多看这种开源软件,学习CAD的开发原理。

本文主要介绍开源软件QCAD的插件Plugin的开发方法。

2.Javascript

由于QCAD的菜单、交互都提供了Javascript的封装,所以QCAD的大部分功能都是用Javascript脚本实现。使Javascript脚本对QCAD进行开发也是QCAD作者推荐的方式。

https://www.qcad.org/doc/qcad/latest/developer/_script_scope.html

QCAD程序框架提供了一很完整的强大的ECMAScript接口,QCAD几乎所有的功能都可以通过脚本JavaScript来访问。ECMAScript(JavaScript)是很流行且易于学习的一种脚本语言。通过使用JavaScript脚本来扩展QCAD是一种简单高效的方式,扩展的功能包括交互创建、修改工具等等。

用户甚至可以基于QCAD的应用框架开发出一个全新的程序。全新的程序可能是一个控制台工具或包含用户交互的CAD程序:

如下图所示为QCAD中主要模块的功能。Qt主要涉及通用的功能,与CAD没有直接关系。QCAD程序框架QCAD Application Framework提供CAD专用功能,如CAD Core, DXF导入导出、强大的图形视图powerful graphics view等等。脚本ECMAScript可以用来快速的扩展CAD专用功能。QCAD用户接口及所有的交互功能、几乎所有的窗口都是通过脚本实现的。

QCAD包中的qcad.exe就是一个ECMAScript解释器,并且封装了Qt和QCAD的接口。当没有任何ECMAScript脚本的时候,运行qcad.exe将会什么也不做。Qcad.exe默认会查找“scripts/autostart.js”并执行。在QCAD中,autostart.js脚本初始化了所有的ECMAScript工具和用户交互的功能,并启动主程序。

QCAD中几乎所有的窗口、菜单、工具栏都是通过ECMAScript脚本实现。这些脚本位于scripts文件夹中。

用JavaScript脚本开发QCAD插件最好办法就是先在QCAD中创建菜单和工具栏。下面就给出在QCAD中创建菜单和工具栏的步骤。首先要创建文件结构:

l 对于新的顶层菜单,在QCAD目录中的scripts文件夹中创建一个新的文件夹。例如:创建一个“MyScripts”的文件夹;

l 在MyScripts文件夹中创建一个文本文件“MyScripts.js”;

l 在MyScripts文件夹中创建另外一个文件夹来提供一个命令Action,如命名为“MyAction”;

l 在MyAction文件夹中创建一个文本文件MyAction.js,文件名必须和文件夹的名字一致;

文件组织结构如下所示:

将如下JavaScript脚本复制到MyScripts.js文件中:

/**
 * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
 *
 * This file is part of the QCAD project.
 *
 * QCAD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QCAD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with QCAD.
 */
// MyScripts.js
// All actions are derived from class EAction, so we need to
// include this class definition here:
include("../EAction.js");
// Constructor calls base class constructor:
function MyScripts(guiAction) {
    EAction.call(this, guiAction);
}
// Derive class MyScripts from class EAction:
MyScripts.prototype = new EAction();
// This static function returns a new or existing QMenu object.
MyScripts.getMenu = function() {
    // EAction.getMenu is a helper function that returns an existing
    // or new QMenu object with the given title and object name.
    // The object name (here "MyScriptMenu") must be unique.
    return EAction.getMenu(MyScripts.getTitle(), "MyScriptsMenu");
};
// This static function returns a new or existing QToolBar object.
MyScripts.getToolBar = function() {
    // EAction.getToolBar is a helper function that returns an existing
    // or new QToolBar object with the given title and object name.
    // The object name (here "MyScriptToolBar") must be unique.
    return EAction.getToolBar(MyScripts.getTitle(), "MyScriptToolBar");
};
// This static function defines and returns the title of the menu
// and toolbar.
// The qsTr function marks the title as a translatable string.
MyScripts.getTitle = function() {
    return qsTr("My Scripts");
};
// Init creates the menu and toolbar on start.
MyScripts.init = function() {
    MyScripts.getMenu();
    MyScripts.getToolBar();
};

将如下脚本代码复制到MyAction.js文件中:

/**
 * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved.
 *
 * This file is part of the QCAD project.
 *
 * QCAD is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * QCAD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with QCAD.
 */
// MyAction.js
// Include base class definition:
include("../MyScripts.js");

// Constructor calls base class constructor:
function MyAction(guiAction) {
    MyScripts.call(this, guiAction);
}
// Derive class MyAction from class MyScripts:
MyAction.prototype = new MyScripts();
// This function is called immediately after the constructor when the user
// starts this action. For actions that don‘t require any user input (for
// example auto zoom), beginEvent does everything and then terminates the
// action.
MyAction.prototype.beginEvent = function() {
    // call base class implementation of beginEvent:
    MyScripts.prototype.beginEvent.call(this);
    // get main application window:
    var appWin = EAction.getMainWindow();
    // print a message in the console of QCAD:
    appWin.handleUserMessage("MyAction() is running...");

    // terminate this action immediately:
    this.terminate();
};
// MyAction.init() is called by QCAD to initialize the action and create
// the menu / toolbar for it.
MyAction.init = function(basePath) {
    // Create a new RGuiAction (extended QAction):
    var action = new RGuiAction("&My Action", RMainWindowQt.getMainWindow());
    // This action requires a document to be open. If no document is
    // open, the menu and tool button are grayed out:
    action.setRequiresDocument(true);
    // Define the script file that is executed when this action is
    // launched:
    action.setScriptFile(basePath + "/MyAction.js");
    // Set the icon that is shown in the toolbar and on some platforms
    // also in the menu:
    action.setIcon(basePath + "/MyAction.svg");
    // Set the command(s) that can be used on the command line to
    // launch this action:
    action.setDefaultCommands(["myaction"]);
    // Define the sort order of this action. Menus and tool buttons are
    // ordered by these values:
    action.setGroupSortOrder(80100);
    action.setSortOrder(200);
    // Set list of widgets this action is added to
    // (menus, tool bars, CAD tool bar panels):
    action.setWidgetNames(["MyScriptsMenu"]);
};

启动QCAD可以发现在菜单上有了MyScripts,如下图所示:

点击MyAction菜单,会在命令窗口中输出测试文字。

3.C++

既然QCAD的是基于Qt开发的,理所当然地应该支持C++开发,只是C++开发方式需要与JavaScript相结合。因为QCAD中一些交互功能封装到JavaScript脚本中了,所以只能通过在JavaScript中调用C++。这种方式QCAD也提供了一个例子,位于源码的

support\examples\exampleplugin中。主要是将C++的类暴露给JavaScript,使在JavaScript中可以调用C++的类及其方法。只将头文件源码列出如下:RExamplePlugin.h

#include <QDebug>
#include <QObject>
#include <QScriptEngine>
#include <QStringList>

#include "RActionAdapter.h"
#include "RDocumentInterface.h"
#include "RGuiAction.h"
#include "RMainWindow.h"
#include "RPluginInterface.h"

class MyAction : public RActionAdapter {
public:
    MyAction(RGuiAction* guiAction) : RActionAdapter(guiAction) {}

    static void factory(RGuiAction* guiAction) {
        qDebug() << "MyAction::factory";
        if (guiAction==NULL) {
            qDebug("guiAction is NULL");
            return;
        }
        RDocumentInterface* di = RMainWindow::getDocumentInterfaceStatic();

        if (di==NULL) {
            qDebug("di is NULL");
            return;
        }

        di->setCurrentAction(new MyAction(guiAction));
    }

    virtual void beginEvent() {
        qDebug() << "MyAction::beginEvent";
    }
};

class MyClass : public QObject {
Q_OBJECT
public:
    MyClass() : QObject(), i(0), d(0.0) {}

    virtual int getInt() const {
        return i;
    }

    virtual double getDouble() const {
        return d;
    }

    virtual QString getString() const {
        return s;
    }

    virtual void setInt(int v) {
        i = v;
    }

    virtual void setDouble(int v) {
        d = v;
    }

    virtual void setString(const QString& v) {
        s = v;
    }

    void emitSignal() {
        emit mySignal(i);
    }

signals:
    void mySignal(int code);

private:
    int i;
    double d;
    QString s;
};

Q_DECLARE_METATYPE(MyClass*)

/**
 * Script binding for MyClass.
 */
class EcmaMyClass {
public:
    static void initEcma(QScriptEngine& engine);

    static QScriptValue createMyClass(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue myClassToString(QScriptContext *context, QScriptEngine *engine);
    static MyClass* getSelfMyClass(const QString& fName, QScriptContext* context);

    static QScriptValue getInt(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue getDouble(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue getString(QScriptContext* context, QScriptEngine* engine);

    static QScriptValue setInt(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue setDouble(QScriptContext* context, QScriptEngine* engine);
    static QScriptValue setString(QScriptContext* context, QScriptEngine* engine);

    static QScriptValue emitSignal(QScriptContext* context, QScriptEngine* engine);
};

class RExamplePlugin : public QObject, public RPluginInterface
{
    Q_OBJECT
    Q_INTERFACES(RPluginInterface)
#if QT_VERSION >= 0x050000
    Q_PLUGIN_METADATA(IID "org.qcad.exampleplugin")
#endif

public:
    virtual bool init();
    virtual void uninit(bool) {}
    virtual void postInit(InitStatus status);
    virtual void initScriptExtensions(QScriptEngine& engine);
    virtual RPluginInfo getPluginInfo();

};

从上述源码可以看出,通过Qt的Plguin机制将C++的类暴露给JavaScript,从而在JavaScript中使用C++的功能。如下图所示为在QCAD中通过JavaScript调用C++来显示一个三维视图的窗口。

4.Conclusion

综上所述,QCAD二次开发的方式主要是以JavaScript为主。如果要在QCAD中使用C++,或者是使用C++的第三方库,只能是将相关的C++类暴露给JavaScirpt,这样开发才是最简单的。如果纯用C++开发,一些交互功能是封装在JavaScript中,反而效率不高。

通过在QCAD中使用OpenCASCADE之类的三维几何内核,可以实现一些建模、出图的功能。

5.References

1. https://www.qcad.org/doc/qcad/latest/developer/_menus_and_tool_bars.html

2. https://www.qcad.org/doc/qcad/latest/developer/index.html#what_is

3. https://www.qcad.org/rsforum/viewforum.php?f=30&sid=8621b8249232845e54252ef7fa6b34ae

4. JavaScript 高级程序设计

5. C++ GUI Programming with Qt

原文地址:https://www.cnblogs.com/opencascade/p/QCAD_Plugin.html

时间: 2024-10-09 19:39:46

QCAD Plugin 开发的相关文章

[Cordova] Plugin开发架构

[Cordova] Plugin开发架构 问题情景 开发Cordova Plugin的时候,侦错Native Code是一件让人困扰的事情,因为Cordova所提供的错误讯息并没有那么的完整.常常需要花费大量的时间与精神之后,才发现只是一个字母打错,无形中降低了开发的效率. 解决方案 为了增加Cordova Plugin开发的效率,开发人员可以套用下列的开发架构,来加速开发: 将实际提供功能的Native Code,使用IDE封装为Native Library.在这个步骤中,使用IDE封装Nat

[Cordova] Plugin开发入门

[Cordova] Plugin开发入门 Overview Cordova的设计概念,是在APP上透过Web控件来呈现Web页面,让Web开发人员可以操作熟悉的语言.工具来开发APP.使用Web页面来呈现功能内容,的确可以满足大部分的功能需求,但是因为APP的使用情景毕竟有别于Web,某些APP的功能需求像是:拨打电话.扫描条形码...等等,无法单纯使用Web开发技术就能实现. 为了让Web页面能够满足更多的APP功能需求,Cordova提供了Plugin机制,让Web页面能够挂载并调用Nati

android studio plugin开发和使用

最近由于项目需要要开发一个android studio的插件(自动化管理Android项目的配置),这里就描述一下整个过程,从开发到应用. 首先,我不知道如何在Android studio上直接开发插件,然而Android studio是基于Intellij IDEA,所以我想在Intellij 上开发,然后到处jar包,在studio上应用.因为我发现https://plugins.jetbrains.com/好多插件在Android studio和Intellij 上都是可以用的,最后验证也

12-4【全栈皆宜】Flutter Plugin开发指南-Dart端实现

开发flutter的插件,首先来定义Dart端的接口. 创建plugin目录 创建类 这里需要用到dart端和native端通讯的channel.首先需要导入services的包 指定channe的名字要和native端统一,否则找不到 提供几个接口. 首先是start方法,参数params为可选的 停止录音的接口.和取消录音的方法 结束 原文地址:https://www.cnblogs.com/wangjunwei/p/12311152.html

12-5【打通Flutter与Android的任督二脉】Flutter Plugin开发指南-Android端实现-1

建议先学 已经实现好了这几个类. 首先以安卓模式来打开我们的项目 在asr这个目录下,导入几个类 这几个类其实是下载的百度AI语音demo里面所提供的,里面已经提供了对百度AI的使用 首先复制这个IRecogListener IRecogListener复制过来改个名字叫做OnasrListener 这里用到了RecogResult 从demo工程里面导入RecogResult 直接复制过来即可 导入RecogEventAdpter 导入后,这里的Listener需要改成OnAsrListene

【Mybtais】Mybatis 插件 Plugin开发(一)动态代理步步解析

需求: 对原有系统中的方法进行'拦截',在方法执行的前后添加新的处理逻辑. 分析: 不是办法的办法就是,对原有的每个方法进行修改,添加上新的逻辑:如果需要拦截的方法比较少,选择此方法到是会节省成本.但是面对成百上千的方法怎么办?此时需要用到动态代理来实现. 场景: 例如:对原有的系统添加日志记录.添加性能分析等等... 举例: 如下,需要对Sleep对象的sleep方法进行"拦截",并在此方法的执行前后添加新的逻辑.想知道'睡觉前干了什么?睡觉后干了什么?' interface Sle

12-6【打通Flutter与Android的任督二脉】Flutter Plugin开发指南-Android端实现-2

重点实现AsrPlugin, 需要打印log.就需要一个TAG,这里定义了一个TAG 然后来添加一个方法 在里面实例化MethodChannel和我们的Dart端进行关联,参数接收一个BinaryMessager 用register.messager()获取到Message 然后来实例化Plugin.传递register 构造方法,接收一个register 重要的一步 setMethodCallHandler这样我们才能处理,来自dart端的消息. 在构造方法内获取activity.要获取act

JQuery Plugin 开发

jquery 中有2个重要的API是和插件编写相关的. jQuery.extend(object)    即 $.extend(object) 这个函数是用来扩展 jQuery 本身, 也就是扩展 "$" 的. jQuery.fn.extend(object) 即 $.fn.extend(object) 这个函数用来为 jQuery 对象提供新的方法. 所谓 jQuery 对象, 最常见的我们平时通过jQuery选择器获取的对象, 比如: $("#id"), $(&

磕磕碰碰的Chrome之plugin开发

前言 在Firefox下可用的npapi插件,在chrome下调用时遇到问题,于是尝试研究chrome下的ppapi插件,一路上真是磕磕碰碰,波折不断啊. 阶段一.复用npapi 尝试将npapi直接用在chrome中,结果在chrome://plugins页下能看到npapi的插件,调试时看到插件对象被创建了,但是无法调用插件对象的函数. 之后各种尝试未果,网上一搜貌似2013年的时候chrome就开始逐渐不支持npapi了,于是只有尝试ppapi... 阶段二.创建ppapi 建简单的ppa