Skip to content

Commit

Permalink
+ implement class PythonGroupCommand
Browse files Browse the repository at this point in the history
  • Loading branch information
wwmayer committed Jun 27, 2015
1 parent 38fffd8 commit 882ecd3
Show file tree
Hide file tree
Showing 4 changed files with 332 additions and 33 deletions.
39 changes: 9 additions & 30 deletions src/Gui/ApplicationPy.cpp
Expand Up @@ -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());
Expand All @@ -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;
}
Expand Down
234 changes: 233 additions & 1 deletion src/Gui/Command.cpp
Expand Up @@ -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)
Expand Down Expand Up @@ -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
//===========================================================================
Expand Down
49 changes: 48 additions & 1 deletion src/Gui/Command.h
Expand Up @@ -322,7 +322,7 @@ class GuiExport Command : public CommandBase
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:
Expand Down Expand Up @@ -364,6 +364,53 @@ class PythonCommand: public Command
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
Expand Down

0 comments on commit 882ecd3

Please sign in to comment.