建立Workbench

From FreeCAD Documentation
Revision as of 09:26, 8 March 2016 by Randy19962 (talk | contribs)
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

本頁將告訴你如何增加一個新的workbench到FreeCAD介面。Workbenches是一個FreeCAD指令的容器。我們可以用Python、C++或者是混合使用前兩種語言來撰寫。而混合使用的好處是可以擁有C++的速度和Python的 flexibility 。然而在所有的workbench都會藉由兩個檔案來開啟他們

workbench 架構

基本上這很簡單:需要再Mod資料夾裏面建立一個新的資料夾,並給資料夾一個任意的名字,並在資料夾中放入 Init.py 檔, 以及, 依照需求選擇放或不放一個 InitGui.py 檔. Init 會在FreeCAD開始的時候被執行然後通常會緊接著執行InitGui.py 檔,不過InitGui.py 檔只有在FreeCAD是在圖形化介面(GUI)模式下才會被執行 ,在 console 模式下不會被執行. 這些就是讓FreeCAD在啟動時找到你的workbench並且將他加到介面上所需要做的事情。

在這些檔案裏面,你可以做任何你想做的事。通常使用的方式如下

  • In the Init.py file you just add a couple of things used even when FreeCAD works in console mode, for example the file importers and exporters
  • In the InitGui.py file you usually define a workbench, which contains a name, an icon, and a series of FreeCAD commands (see below). That workbench also defines functions that are executed when FreeCAD loads (you try to do as little as possible there, so you don't slow down the startup), another that gets executed when the workbench is activated (that's where you'll do most of the work), and a third one when the workbench is deactivated (so you can remove things if needed).

C++ workbench 架構

如果你打算要用Python來寫你的workbench程式碼,你只需要將其他Python檔案跟Init.py 和InitGui.py放在一起就好了,而不用再去煩惱其他事情。不過當你是使用C++來撰寫workbench的時候,你必須特別留意並且遵守FreeCAD的一個基本規則:你必須將你的workbench分成App(可以在命令列介面下執行而不需要任何圖形使用者介面)和Gui(只有在FreeCAD在圖形使用者介面下執行時才會被載入)兩個部份。 所以用C++撰寫workbench你幾乎就像是在開發兩個modules,也就是App和Gui這兩個。這兩個模組當然必須能被Python呼叫。任何 FreeCAD module(App或Gui)至少都包含一個module的init 檔。這是典型AppMyModuleGui.cpp檔 :

extern "C" {
    void MyModuleGuiExport initMyModuleGui()  
    {
         if (!Gui::Application::Instance) {
            PyErr_SetString(PyExc_ImportError, "Cannot load Gui module in console application.");
            return;
        }
        try {
            // import other modules this one depends on
            Base::Interpreter().runString("import PartGui");
            // run some python code in the console
            Base::Interpreter().runString("print('welcome to my module!')");
        }
        catch(const Base::Exception& e) {
            PyErr_SetString(PyExc_ImportError, e.what());
            return;
        }
        (void) Py_InitModule("MyModuleGui", MyModuleGui_Import_methods);   /* mod name, table ptr */
        Base::Console().Log("Loading GUI of MyModule... done\n");
    
        // initializes the FreeCAD commands (in another cpp file)
        CreateMyModuleCommands();
    
        // initializes workbench and object definitions
        MyModuleGui::Workbench::init();
        MyModuleGui::ViewProviderSomeCustomObject::init();
    
         // add resources and reloads the translators
        loadMyModuleResource();
    }
}

The Init.py file

# FreeCAD init script of XXX module  

#***************************************************************************
#*   (c) John Doe john@doe.com 2015                                        *   
#*                                                                         *
#*   This file is part of the FreeCAD CAx development system.              *
#*                                                                         *
#*   This program is free software; you can redistribute it and/or modify  *
#*   it under the terms of the GNU Lesser General Public License (LGPL)    *
#*   as published by the Free Software Foundation; either version 2 of     *
#*   the License, or (at your option) any later version.                   *
#*   for detail see the LICENCE text file.                                 *
#*                                                                         *
#*   FreeCAD 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 Lesser General Public License for more details.                   *
#*                                                                         *
#*   You should have received a copy of the GNU Library General Public     *
#*   License along with FreeCAD; if not, write to the Free Software        *
#*   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
#*   USA                                                                   *
#*                                                                         *
#***************************************************************************/

FreeCAD.addImportType("My own format (*.own)","importOwn")
FreeCAD.addExportType("My own format (*.own)","importOwn")
print "I am executing some stuff here when FreeCAD starts!"

你可以為你的workbench選擇任何你喜歡的license,不過要注意的是,如果你希望見到你的workbench被FreeCAD原始碼整合並且發佈,他必須是LGPL2+就如同上面的範例。 FreeCAD.addImportType() 和addEXportType() 函式 allow you to give the name and extension of a file type, and a python module responsible for its import. 在上面的例子中,一個"importOwn.py" module 將會處理.own檔。請看 Code snippets 來獲得更多的例子

Python workbenches

class MyWorkbench (Workbench):

    MenuText = "My Workbench"
    ToolTip = "A description of my workbench"
    Icon = """paste here the contents of a 16x16 xpm icon"""

    def Initialize(self):
        "This function is executed when FreeCAD starts"
        import MyModuleA, MyModuleB # import here all the needed files that create your FreeCAD commands
        self.list = ["MyCommand1, MyCommand2"] # A list of command names created in the line above
        self.appendToolbar("My Commands",self.list) # creates a new toolbar with your commands
        self.appendMenu("My New Menu",self.list) # creates a new menu
        self.appendMenu(["An existing Menu","My submenu"],self.list) # appends a submenu to an existing menu

    def Activated(self):
        "This function is executed when the workbench is activated"
        return

    def Deactivated(self):
        "This function is executed when the workbench is deactivated"
        return

    def ContextMenu(self, recipient):
        "This is executed whenever the user right-clicks on screen"
        # "recipient" will be either "view" or "tree"
        self.appendContextMenu("My commands",self.list) # add commands to the context menu

    def GetClassName(self): 
        # this function is mandatory if this is a full python workbench
        return "Gui::PythonWorkbench"
       
Gui.addWorkbench(MyWorkbench())

Other than that, you can do anything you want: You could put your whole workbench code inside the InitGui.py if you want, but it is usually more convenient to place the different functions of your workbench in separate files. So those files are smaller and easier to read. Then you import those files into your InitGui.py file. You can organize those files the way you want, a good example is one for each FreeCAD command you add.

C++ workbenches

If you are going to code your workbench in C++, you will probably want to code the workbench definition itself in C++ too (although it is not necessary: You could also code only the tools in C++, and leave the workbench definition in python). In that case, the InitGui.py file becomes very simple: It might contain just one line:

import MyModuleGui

where MyModule is your complete C++ workbench, including the commands and workbench definition.

Coding C++ workbenches works in a pretty similar way. This is a typical Workbench.cpp file to include in the Gui part of your module:

namespace MyModuleGui {
    class MyModuleGuiExport Workbench : public Gui::StdWorkbench
    {
        TYPESYSTEM_HEADER();

    public:
        Workbench();
        virtual ~Workbench();

        virtual void activated();
        virtual void deactivated();

    protected:
        Gui::ToolBarItem* setupToolBars() const;
        Gui::MenuItem*    setupMenuBar() const;
    };
}

FreeCAD commands

FreeCAD commands are the basic building block of the FreeCAD interface. They can appear as a button on toolbars, and as a menu entry in menus. But it is the same command. A command is a simple python class, that must contain a couple of predefined attributes and functions, that define the name of the command, its icon, and what to do when the command is activated.

Python command definition

class My_Command_Class():
    """My new command"""

    def GetResources(self):
        return {'Pixmap'  : 'My_Command_Icon', # the name of a svg file available in the resources
                'Accel' : "Shift+S", # a default shortcut (optional)
                'MenuText': "My New Command"
                'ToolTip' : "What my new command does"}

    def Activated(self):
        "Do something here"
        return

    def IsActive(self):
        """Here you can define if the command must be active or not (greyed) if certain conditions
        are met or not. This function is optional."""
        return True

FreeCADGui.addCommand('My_Command',My_Command_Class())

C++ command definition

Similarly, you can code your commands in C++, typically have a Commands.cpp file in your Gui module. This is a typical Commands.cpp file:

DEF_STD_CMD_A(CmdMyCommand);

CmdMyCommand::CmdMyCommand()
  :Command("My_Command")
{
  sAppModule    = "MyModule";
  sGroup        = QT_TR_NOOP("MyModule");
  sMenuText     = QT_TR_NOOP("Runs my command...");
  sToolTipText  = QT_TR_NOOP("Describes what my command does");
  sWhatsThis    = QT_TR_NOOP("Describes what my command does");
  sStatusTip    = QT_TR_NOOP("Describes what my command does");
  sPixmap       = "some_svg_icon_from_my_resource";
}

void CmdMyCommand::activated(int iMsg)
{
    openCommand("My Command");
    doCommand(Doc,"print('Hello, world!')");
    commitCommand();
    updateActive();
}

bool CmdMyCommand::isActive(void)
{
  if( getActiveGuiDocument() )
    return true;
  else
    return false;
}

void CreateMyModuleCommands(void)
{
    Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
    rcCmdMgr.addCommand(new CmdMyCommand());
}