Gui: Add CommandAction descriptor object to access commands action

This commit is contained in:
marioalexis
2023-04-03 01:12:15 -03:00
committed by wwmayer
parent 2b68404892
commit ae480d70cd
7 changed files with 284 additions and 2 deletions

View File

@@ -62,6 +62,7 @@
#include "AxisOriginPy.h"
#include "BitmapFactory.h"
#include "Command.h"
#include "CommandActionPy.h"
#include "CommandPy.h"
#include "Control.h"
#include "DlgSettingsCacheDirectory.h"
@@ -469,6 +470,10 @@ Application::Application(bool GUIenabled)
Py::Object(Gui::TaskView::ControlPy::getInstance(), true));
Gui::TaskView::TaskDialogPy::init_type();
CommandActionPy::init_type();
Base::Interpreter().addType(CommandActionPy::type_object(),
module, "CommandAction");
Base::Interpreter().addType(&LinkViewPy::Type, module, "LinkView");
Base::Interpreter().addType(&AxisOriginPy::Type, module, "AxisOrigin");
Base::Interpreter().addType(&CommandPy::Type, module, "Command");

View File

@@ -378,6 +378,7 @@ SET(Command_CPP_SRCS
CommandPyImp.cpp
ShortcutManager.cpp
CommandCompleter.cpp
CommandActionPy.cpp
)
SET(Command_SRCS
${Command_CPP_SRCS}
@@ -387,6 +388,7 @@ SET(Command_SRCS
CommandT.h
ShortcutManager.h
CommandCompleter.h
CommandActionPy.h
)
SOURCE_GROUP("Command" FILES ${Command_SRCS})

View File

@@ -1259,6 +1259,11 @@ PythonCommand::PythonCommand(const char* name, PyObject * pcPyCommand, const cha
type += int(NoTransaction);
eType = type;
}
auto& rcCmdMgr = Gui::Application::Instance->commandManager();
connPyCmdInitialized = rcCmdMgr.signalPyCmdInitialized.connect(
boost::bind(&PythonCommand::onActionInit, this));
}
PythonCommand::~PythonCommand()
@@ -1432,6 +1437,25 @@ bool PythonCommand::isChecked() const
}
}
void PythonCommand::onActionInit() const
{
try {
Base::PyGILStateLocker lock;
Py::Object cmd(_pcPyCommand);
if (cmd.hasAttr("OnActionInit")) {
Py::Callable call(cmd.getAttr("OnActionInit"));
Py::Tuple args;
Py::Object ret = call.apply(args);
}
}
catch(Py::Exception& e) {
Base::PyGILStateLocker lock;
e.clear();
}
connPyCmdInitialized.disconnect();
}
//===========================================================================
// PythonGroupCommand
//===========================================================================
@@ -1466,6 +1490,11 @@ PythonGroupCommand::PythonGroupCommand(const char* name, PyObject * pcPyCommand)
type += int(ForEdit);
eType = type;
}
auto& rcCmdMgr = Gui::Application::Instance->commandManager();
connPyCmdInitialized = rcCmdMgr.signalPyCmdInitialized.connect(
boost::bind(&PythonGroupCommand::onActionInit, this));
}
PythonGroupCommand::~PythonGroupCommand()
@@ -1732,6 +1761,25 @@ bool PythonGroupCommand::hasDropDownMenu() const
}
}
void PythonGroupCommand::onActionInit() const
{
try {
Base::PyGILStateLocker lock;
Py::Object cmd(_pcPyCommand);
if (cmd.hasAttr("OnActionInit")) {
Py::Callable call(cmd.getAttr("OnActionInit"));
Py::Tuple args;
Py::Object ret = call.apply(args);
}
}
catch(Py::Exception& e) {
Base::PyGILStateLocker lock;
e.clear();
}
connPyCmdInitialized.disconnect();
}
//===========================================================================
// CommandManager
//===========================================================================

View File

@@ -686,8 +686,6 @@ protected:
bool isActive() override;
/// Get the help URL
const char* getHelpUrl() const override;
/// Creates the used Action
Action * createAction() override;
//@}
public:
@@ -710,12 +708,18 @@ public:
protected:
/// Returns the resource values
const char* getResource(const char* sName) const;
/// Creates the used Action
Action * createAction() override;
/// a pointer to the Python command object
PyObject * _pcPyCommand;
/// the command object resource dictionary
PyObject * _pcPyResourceDict;
/// the activation sequence
std::string Activation;
//// set the parameters on action creation
void onActionInit() const;
boost::signals2::connection connPyCmdInitialized;
};
/** The Python group command class
@@ -761,10 +765,14 @@ public:
protected:
/// Returns the resource values
const char* getResource(const char* sName) const;
//// set the parameters on action creation
void onActionInit() const;
/// a pointer to the Python command object
PyObject * _pcPyCommand;
/// the command object resources
PyObject * _pcPyResource;
boost::signals2::connection connPyCmdInitialized;
};
@@ -887,6 +895,9 @@ public:
/// Signal on any addition or removal of command
boost::signals2::signal<void ()> signalChanged;
/// Signal to Python command on first workbench activation
boost::signals2::signal<void ()> signalPyCmdInitialized;
/**
* Returns a pointer to a conflicting command, or nullptr if there is no conflict.
* In the case of multiple conflicts, only the first is returned.

156
src/Gui/CommandActionPy.cpp Normal file
View File

@@ -0,0 +1,156 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2023 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* 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 Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#include <Base/PythonTypeExt.h>
#include "Command.h"
#include "Action.h"
#include "PythonWrapper.h"
#include "CommandActionPy.h"
#include "CommandPy.h"
using namespace Gui;
CommandActionPy::CommandActionPy(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kwds)
: Py::PythonClass<CommandActionPy>::PythonClass(self, args, kwds)
{
const char* name;
if (!PyArg_ParseTuple(args.ptr(), "s", &name)) {
throw Py::Exception();
}
cmdName = name;
cmd = Application::Instance->commandManager().getCommandByName(name);
}
CommandActionPy::~CommandActionPy()
{
}
Py::Object CommandActionPy::getAction()
{
if (!cmd) {
cmd = Application::Instance->commandManager().getCommandByName(cmdName.c_str());
}
if (cmd) {
Action* action = cmd->getAction();
PythonWrapper wrap;
wrap.loadWidgetsModule();
return wrap.fromQObject(action->action());
}
else {
return Py::None();
}
}
Py::Object CommandActionPy::getCommand()
{
if (!cmd) {
cmd = Application::Instance->commandManager().getCommandByName(cmdName.c_str());
}
if (cmd) {
auto cmdPy = new CommandPy(cmd);
return Py::asObject(cmdPy);
}
return Py::None();
}
PYCXX_NOARGS_METHOD_DECL(CommandActionPy, getCommand)
void CommandActionPy::init_type()
{
Base::PythonTypeExt ext(behaviors());
behaviors().name("Gui.CommandAction");
behaviors().doc("Descriptor to access the action of the commands");
behaviors().supportRepr();
behaviors().supportGetattro();
behaviors().supportSetattro();
ext.set_tp_descr_get(&CommandActionPy::descriptorGetter);
ext.set_tp_descr_set(&CommandActionPy::descriptorSetter);
PYCXX_ADD_NOARGS_METHOD(getCommand, getCommand, "Descriptor associated command");
behaviors().readyType();
}
PyObject* CommandActionPy::descriptorGetter(PyObject* self, PyObject* /*obj*/, PyObject* /*type*/)
{
auto cmdAction = Py::PythonClassObject<CommandActionPy>(self).getCxxObject();
return Py::new_reference_to(cmdAction->getAction());
}
int CommandActionPy::descriptorSetter(PyObject* /*self*/, PyObject* /*obj*/, PyObject* value)
{
if (value) {
PyErr_SetString(PyExc_AttributeError, "Can't overwrite command action");
}
else {
PyErr_SetString(PyExc_AttributeError, "Can't delete command action");
}
return -1;
}
Py::Object CommandActionPy::repr()
{
std::stringstream s;
s << this->cmdName << " command action descriptor";
return Py::String(s.str());
}
Py::Object CommandActionPy::getattro(const Py::String& attr_)
{
std::string attr = static_cast<std::string>(attr_);
Py::Dict d;
d["name"] = Py::String(this->cmdName);
if (attr == "__dict__") {
return d;
}
else if (attr == "name") {
return d["name"];
}
else {
return genericGetAttro(attr_);
}
}
int CommandActionPy::setattro(const Py::String& attr_, const Py::Object& value)
{
std::string attr = static_cast<std::string>(attr_);
if (attr == "name" && value.isString()) {
cmdName = static_cast<std::string>(Py::String(value));
cmd = Application::Instance->commandManager().getCommandByName(cmdName.c_str());
}
else {
return genericSetAttro(attr_, value);
}
return 0;
}

59
src/Gui/CommandActionPy.h Normal file
View File

@@ -0,0 +1,59 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
/***************************************************************************
* Copyright (c) 2023 Mario Passaglia <mpassaglia[at]cbc.uba.ar> *
* *
* This file is part of FreeCAD. *
* *
* FreeCAD is free software: you can redistribute it and/or modify it *
* under the terms of the GNU Lesser General Public License as *
* published by the Free Software Foundation, either version 2.1 of the *
* License, or (at your option) any later version. *
* *
* 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 Lesser General Public *
* License along with FreeCAD. If not, see *
* <https://www.gnu.org/licenses/>. *
* *
**************************************************************************/
#ifndef GUI_COMMANDACTIONPY_H
#define GUI_COMMANDACTIONPY_H
#include <CXX/Extensions.hxx>
namespace Gui
{
class CommandActionPy : public Py::PythonClass<CommandActionPy>
{
public:
static void init_type();
CommandActionPy(Py::PythonClassInstance* self, Py::Tuple& args, Py::Dict& kdws);
~CommandActionPy() override;
Py::Object getCommand();
protected:
static PyObject* descriptorGetter(PyObject* self, PyObject* obj, PyObject* type);
static int descriptorSetter(PyObject* self, PyObject* obj, PyObject* value);
Py::Object repr() override;
Py::Object getattro(const Py::String& attr) override;
int setattro(const Py::String& attr_, const Py::Object& value) override;
Py::Object getAction();
private:
std::string cmdName;
Command* cmd = nullptr;
};
} // namespace Gui
#endif // GUI_COMMANDACTIONPY_H

View File

@@ -393,6 +393,7 @@ void Workbench::addPermanentMenuItems(MenuItem* mb) const
void Workbench::activated()
{
Application::Instance->commandManager().signalPyCmdInitialized();
}
void Workbench::deactivated()