Gui: Add CommandAction descriptor object to access commands action
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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})
|
||||
|
||||
|
||||
@@ -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
|
||||
//===========================================================================
|
||||
|
||||
@@ -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
156
src/Gui/CommandActionPy.cpp
Normal 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
59
src/Gui/CommandActionPy.h
Normal 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
|
||||
@@ -393,6 +393,7 @@ void Workbench::addPermanentMenuItems(MenuItem* mb) const
|
||||
|
||||
void Workbench::activated()
|
||||
{
|
||||
Application::Instance->commandManager().signalPyCmdInitialized();
|
||||
}
|
||||
|
||||
void Workbench::deactivated()
|
||||
|
||||
Reference in New Issue
Block a user