+ implement class PythonGroupCommand

This commit is contained in:
wmayer 2015-06-27 17:21:02 +02:00
parent 38fffd8218
commit 882ecd3ce3
4 changed files with 332 additions and 33 deletions

View File

@ -812,37 +812,16 @@ PyObject* Application::sAddCommand(PyObject * /*self*/, PyObject *args,PyObject
PyObject* pcCmdObj;
if (!PyArg_ParseTuple(args, "sO|s", &pName,&pcCmdObj,&pSource)) // convert args: Python->C
return NULL; // NULL triggers exception
#if 0
std::string source = (pSource ? pSource : "");
if (source.empty()) {
try {
Py::Module module(PyImport_ImportModule("inspect"),true);
Py::Dict dict = module.getDict();
Py::Callable call(dict.getItem("getsourcelines"));
Py::Tuple arg(1);
arg.setItem(0, Py::Object(pcCmdObj).getAttr("Activated"));
Py::Tuple tuple(call.apply(arg));
Py::List lines(tuple[0]);
int pos=0;
std::string code = (std::string)(Py::String(lines[1]));
while (code[pos] == ' ' || code[pos] == '\t')
pos++;
for (Py::List::iterator it = lines.begin()+1; it != lines.end(); ++it) {
Py::String str(*it);
source += ((std::string)str).substr(pos);
}
}
catch (Py::Exception& e) {
e.clear();
}
}
Application::Instance->commandManager().addCommand(new PythonCommand(pName,pcCmdObj,source.c_str()));
#else
try {
Application::Instance->commandManager().addCommand(new PythonCommand(pName,pcCmdObj,pSource));
Base::PyGILStateLocker lock;
Py::Object cmd(pcCmdObj);
if (cmd.hasAttr("GetCommands")) {
Application::Instance->commandManager().addCommand(new PythonGroupCommand(pName, pcCmdObj));
}
else {
Application::Instance->commandManager().addCommand(new PythonCommand(pName, pcCmdObj, pSource));
}
}
catch (const Base::Exception& e) {
PyErr_SetString(Base::BaseExceptionFreeCADError, e.what());
@ -852,7 +831,7 @@ PyObject* Application::sAddCommand(PyObject * /*self*/, PyObject *args,PyObject
PyErr_SetString(Base::BaseExceptionFreeCADError, "Unknown C++ exception raised in Application::sAddCommand()");
return 0;
}
#endif
Py_INCREF(Py_None);
return Py_None;
}

View File

@ -793,7 +793,7 @@ void MacroCommand::save()
// PythonCommand
//===========================================================================
PythonCommand::PythonCommand(const char* name,PyObject * pcPyCommand, const char* pActivationString)
PythonCommand::PythonCommand(const char* name, PyObject * pcPyCommand, const char* pActivationString)
: Command(name),_pcPyCommand(pcPyCommand)
{
if (pActivationString)
@ -945,6 +945,238 @@ const char* PythonCommand::getAccel() const
return getResource("Accel");
}
//===========================================================================
// PythonGroupCommand
//===========================================================================
PythonGroupCommand::PythonGroupCommand(const char* name, PyObject * pcPyCommand)
: Command(name),_pcPyCommand(pcPyCommand)
{
sGroup = "Python";
Py_INCREF(_pcPyCommand);
// call the method "GetResources()" of the command object
_pcPyResource = Interpreter().runMethodObject(_pcPyCommand, "GetResources");
// check if the "GetResources()" method returns a Dict object
if (!PyDict_Check(_pcPyResource)) {
throw Base::TypeError("PythonGroupCommand::PythonGroupCommand(): Method GetResources() of the Python "
"command object returns the wrong type (has to be Py Dictonary)");
}
// check for command type
std::string cmdType = getResource("CmdType");
if (!cmdType.empty()) {
int type = 0;
if (cmdType.find("AlterDoc") != std::string::npos)
type += int(AlterDoc);
if (cmdType.find("Alter3DView") != std::string::npos)
type += int(Alter3DView);
if (cmdType.find("AlterSelection") != std::string::npos)
type += int(AlterSelection);
if (cmdType.find("ForEdit") != std::string::npos)
type += int(ForEdit);
eType = type;
}
}
PythonGroupCommand::~PythonGroupCommand()
{
Base::PyGILStateLocker lock;
Py_DECREF(_pcPyCommand);
}
void PythonGroupCommand::activated(int iMsg)
{
try {
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
assert(iMsg < a.size());
QAction* act = a[iMsg];
Base::PyGILStateLocker lock;
Py::Object cmd(_pcPyCommand);
if (cmd.hasAttr("Activated")) {
Py::Callable call(cmd.getAttr("Activated"));
Py::Tuple args(1);
args.setItem(0, Py::Int(iMsg));
Py::Object ret = call.apply(args);
}
// If the command group doesn't implement the 'Activated' method then invoke the command directly
else {
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
rcCmdMgr.runCommandByName(act->property("CommandName").toByteArray());
}
// Since the default icon is reset when enabing/disabling the command we have
// to explicitly set the icon of the used command.
pcAction->setIcon(a[iMsg]->icon());
}
catch(Py::Exception&) {
Base::PyGILStateLocker lock;
Base::PyException e;
Base::Console().Error("Running the Python command '%s' failed:\n%s\n%s",
sName, e.getStackTrace().c_str(), e.what());
}
}
bool PythonGroupCommand::isActive(void)
{
try {
Base::PyGILStateLocker lock;
Py::Object cmd(_pcPyCommand);
if (cmd.hasAttr("IsActive")) {
Py::Callable call(cmd.getAttr("IsActive"));
Py::Tuple args;
Py::Object ret = call.apply(args);
// if return type is not boolean or not true
if (!PyBool_Check(ret.ptr()) || ret.ptr() != Py_True)
return false;
}
}
catch(Py::Exception& e) {
Base::PyGILStateLocker lock;
e.clear();
return false;
}
return true;
}
Action * PythonGroupCommand::createAction(void)
{
Gui::ActionGroup* pcAction = new Gui::ActionGroup(this, Gui::getMainWindow());
pcAction->setDropDownMenu(true);
applyCommandData(this->getName(), pcAction);
int defaultId = 0;
try {
Base::PyGILStateLocker lock;
Py::Object cmd(_pcPyCommand);
Py::Callable call(cmd.getAttr("GetCommands"));
Py::Tuple args;
Py::Tuple ret(call.apply(args));
for (Py::Tuple::iterator it = ret.begin(); it != ret.end(); ++it) {
Py::String str(*it);
QAction* cmd = pcAction->addAction(QString());
cmd->setProperty("CommandName", QByteArray(static_cast<std::string>(str).c_str()));
}
if (cmd.hasAttr("GetDefaultCommand")) {
Py::Callable call2(cmd.getAttr("GetDefaultCommand"));
Py::Int def(call2.apply(args));
defaultId = static_cast<int>(def);
}
}
catch(Py::Exception&) {
Base::PyGILStateLocker lock;
Base::PyException e;
Base::Console().Error("createAction() of the Python command '%s' failed:\n%s\n%s",
sName, e.getStackTrace().c_str(), e.what());
}
_pcAction = pcAction;
languageChange();
if (strcmp(getResource("Pixmap"),"") != 0) {
pcAction->setIcon(Gui::BitmapFactory().pixmap(getResource("Pixmap")));
}
else {
QList<QAction*> a = pcAction->actions();
if (a.size() > defaultId)
pcAction->setIcon(a[defaultId]->icon());
}
pcAction->setProperty("defaultAction", QVariant(defaultId));
return pcAction;
}
void PythonGroupCommand::languageChange()
{
if (!_pcAction)
return;
applyCommandData(this->getName(), _pcAction);
Gui::CommandManager &rcCmdMgr = Gui::Application::Instance->commandManager();
Gui::ActionGroup* pcAction = qobject_cast<Gui::ActionGroup*>(_pcAction);
QList<QAction*> a = pcAction->actions();
for (QList<QAction*>::iterator it = a.begin(); it != a.end(); ++it) {
Gui::Command* cmd = rcCmdMgr.getCommandByName((*it)->property("CommandName").toByteArray());
// Python command use getName as context
if (dynamic_cast<PythonCommand*>(cmd)) {
(*it)->setIcon(Gui::BitmapFactory().pixmap(cmd->getPixmap()));
(*it)->setText(QApplication::translate(cmd->getName(), cmd->getMenuText()));
(*it)->setToolTip(QApplication::translate(cmd->getName(), cmd->getToolTipText()));
(*it)->setStatusTip(QApplication::translate(cmd->getName(), cmd->getStatusTip()));
}
else if (cmd) {
(*it)->setIcon(Gui::BitmapFactory().pixmap(cmd->getPixmap()));
(*it)->setText(QApplication::translate(cmd->className(), cmd->getMenuText()));
(*it)->setToolTip(QApplication::translate(cmd->className(), cmd->getToolTipText()));
(*it)->setStatusTip(QApplication::translate(cmd->className(), cmd->getStatusTip()));
}
}
}
const char* PythonGroupCommand::getHelpUrl(void) const
{
return "";
}
const char* PythonGroupCommand::getResource(const char* sName) const
{
PyObject* pcTemp;
// get the "MenuText" resource string
pcTemp = PyDict_GetItemString(_pcPyResource, sName);
if (!pcTemp)
return "";
if (!PyString_Check(pcTemp)) {
throw Base::ValueError("PythonGroupCommand::getResource(): Method GetResources() of the Python "
"group command object returns a dictionary which holds not only strings");
}
return PyString_AsString(pcTemp);
}
const char* PythonGroupCommand::getWhatsThis() const
{
const char* whatsthis = getResource("WhatsThis");
if (!whatsthis || whatsthis[0] == '\0')
whatsthis = this->getName();
return whatsthis;
}
const char* PythonGroupCommand::getMenuText() const
{
return getResource("MenuText");
}
const char* PythonGroupCommand::getToolTipText() const
{
return getResource("ToolTip");
}
const char* PythonGroupCommand::getStatusTip() const
{
return getResource("StatusTip");
}
const char* PythonGroupCommand::getPixmap() const
{
return getResource("Pixmap");
}
const char* PythonGroupCommand::getAccel() const
{
return getResource("Accel");
}
//===========================================================================
// CommandManager
//===========================================================================

View File

@ -322,7 +322,7 @@ private:
class PythonCommand: public Command
{
public:
PythonCommand(const char* name,PyObject * pcPyCommand, const char* pActivationString);
PythonCommand(const char* name, PyObject * pcPyCommand, const char* pActivationString);
virtual ~PythonCommand() {}
protected:
@ -364,6 +364,53 @@ protected:
std::string Activation;
};
/** The Python group command class
* @see CommandManager
* @author Werner Mayer
*/
class PythonGroupCommand: public Command
{
public:
PythonGroupCommand(const char* name, PyObject * pcPyCommand);
virtual ~PythonGroupCommand();
protected:
/** @name Methods reimplemented for Command Framework */
//@{
/// Method which gets called when activated
virtual void activated(int iMsg);
/// if the command is not always active
virtual bool isActive(void);
/// Get the help URL
const char* getHelpUrl(void) const;
/// Creates the used Action
virtual Action * createAction(void);
//@}
public:
/** @name Methods to get the properties of the command */
//@{
/// Reassigns QAction stuff after the language has changed.
void languageChange();
const char* className() const
{ return "PythonGroupCommand"; }
const char* getWhatsThis () const;
const char* getMenuText () const;
const char* getToolTipText() const;
const char* getStatusTip () const;
const char* getPixmap () const;
const char* getAccel () const;
//@}
protected:
/// Returns the resource values
const char* getResource(const char* sName) const;
/// a pointer to the Python command object
PyObject * _pcPyCommand;
/// the command object resources
PyObject * _pcPyResource;
};
/** The script command class
* This is a special type of command class. Its used to bind a macro or Python script to the

View File

@ -193,6 +193,44 @@ class TemplatePyMod_Cmd6:
def GetResources(self):
return {'Pixmap' : 'python', 'MenuText': 'Create a box', 'ToolTip': 'Use Box feature class which is completely written in Python'}
class TemplatePyGrp_1:
def Activated(self):
import FreeCAD
FreeCAD.Console.PrintMessage("TemplatePyGrp_1\n")
def GetResources(self):
return {'Pixmap' : 'Part_JoinConnect', 'MenuText': 'TemplatePyGrp_1', 'ToolTip': 'Print a message'}
class TemplatePyGrp_2:
def Activated(self):
import FreeCAD
FreeCAD.Console.PrintMessage("TemplatePyGrp_2\n")
def GetResources(self):
return {'Pixmap' : 'Part_JoinEmbed', 'MenuText': 'TemplatePyGrp_2', 'ToolTip': 'Print a message'}
class TemplatePyGrp_3:
def Activated(self):
import FreeCAD
FreeCAD.Console.PrintMessage("TemplatePyGrp_3\n")
def GetResources(self):
return {'Pixmap' : 'Part_JoinCutout', 'MenuText': 'TemplatePyGrp_3', 'ToolTip': 'Print a message'}
class TemplatePyGroup:
"Example group command class"
#def Activated(self, index):
# print "TemplatePyGroup activated ;-) "
def GetCommands(self):
return ("TemplatePyGrp_1", "TemplatePyGrp_2", "TemplatePyGrp_3", "Std_New")
def GetDefaultCommand(self):
return 2
def GetResources(self):
return {'Pixmap' : 'python', 'MenuText': 'Group command', 'ToolTip': 'Example group command'}
#---------------------------------------------------------------------------
# Adds the commands to the FreeCAD command manager
#---------------------------------------------------------------------------
@ -202,4 +240,7 @@ addCommand('TemplatePyMod_Cmd3',TemplatePyMod_Cmd3())
FreeCADGui.addCommand('TemplatePyMod_Cmd4',TemplatePyMod_Cmd4())
FreeCADGui.addCommand('TemplatePyMod_Cmd5',TemplatePyMod_Cmd5())
FreeCADGui.addCommand('TemplatePyMod_Cmd6',TemplatePyMod_Cmd6())
FreeCADGui.addCommand('TemplatePyGrp_1',TemplatePyGrp_1())
FreeCADGui.addCommand('TemplatePyGrp_2',TemplatePyGrp_2())
FreeCADGui.addCommand('TemplatePyGrp_3',TemplatePyGrp_3())
FreeCADGui.addCommand('TemplatePyGroup',TemplatePyGroup())