Gui: Provide ViewProviderFeaturePython selection callbacks

Adds support for Python-based selection callbacks to
`ViewProviderFeaturePython` objects.

It follows the same conventions as `SelectionObserverPython`, follows an
example:

```python
    def setPreselection(self, document, object, element):
        print("setPreselection: %s.%s.%s"%(document, object, element))

    def removePreselection(self, document, object, element):
print("removePreselection: %s.%s.%s"%(document, object,
element))

    def addSelection(self, document, object, element, position):
print("addSelection: %s.%s.%s at %s"%(document, object, element,
str(position)))

    def removeSelection(self,document, object, element):
        print("removeSelection: %s.%s.%s"%(document, object, element))

    def setSelection(self,doc):
        sel = FreeCADGui.Selection.getSelection(doc)
        print("setSelection: %s"%sel)

    def clearSelection(self,doc):
        print("clearSelection\n")
```
This commit is contained in:
tritao
2025-01-15 21:05:07 +00:00
parent 10ad4b7e15
commit ea80310642
6 changed files with 84 additions and 8 deletions

View File

@@ -25,6 +25,7 @@
#ifndef _PreComp_
# include <array>
# include <set>
# include <boost/algorithm/string/predicate.hpp>
# include <QApplication>
#endif
@@ -52,6 +53,7 @@
#include "SelectionFilterPy.h"
#include "SelectionObserverPython.h"
#include "Tree.h"
#include "ViewProvider.h"
#include "ViewProviderDocumentObject.h"
@@ -391,6 +393,25 @@ void SelectionSingleton::enablePickedList(bool enable)
}
}
static void notifyDocumentObjectViewProvider(const SelectionChanges& changes) {
const auto* doc = App::GetApplication().getDocument(changes.pDocName);
if (!doc) {
return;
}
const auto* obj = doc->getObject(changes.pObjectName);
if (!obj) {
return;
}
auto* vp = Application::Instance->getViewProvider(obj);
if (!vp) {
return;
}
vp->onSelectionChanged(changes);
}
void SelectionSingleton::notify(SelectionChanges &&Chng)
{
if(Notifying) {
@@ -401,7 +422,7 @@ void SelectionSingleton::notify(SelectionChanges &&Chng)
NotificationQueue.push_back(std::move(Chng));
while(!NotificationQueue.empty()) {
const auto &msg = NotificationQueue.front();
bool notify;
bool notify = false;
switch(msg.Type) {
case SelectionChanges::AddSelection:
notify = isSelected(msg.pDocName, msg.pObjectName, msg.pSubName, ResolveMode::NoResolve);
@@ -420,6 +441,9 @@ void SelectionSingleton::notify(SelectionChanges &&Chng)
notify = true;
}
if(notify) {
// Notify the view provider of the object.
notifyDocumentObjectViewProvider(msg);
Notify(msg);
try {
signalSelectionChanged(msg);
@@ -1454,6 +1478,21 @@ void SelectionSingleton::clearCompleteSelection(bool clearPreSelect)
clearPreSelect?"Gui.Selection.clearSelection()"
:"Gui.Selection.clearSelection(False)");
// Send the clear selection notification to all view providers associated with the
// objects being deselected.
std::set<ViewProvider*> viewProviders;
for (_SelObj& sel : _SelList) {
if (auto vp = Application::Instance->getViewProvider(sel.pObject)) {
viewProviders.insert(vp);
}
}
for (auto& vp : viewProviders) {
SelectionChanges Chng(SelectionChanges::ClrSelection);
vp->onSelectionChanged(Chng);
}
_SelList.clear();
SelectionChanges Chng(SelectionChanges::ClrSelection);

View File

@@ -32,15 +32,28 @@ using namespace Gui;
std::vector<SelectionObserverPython*> SelectionObserverPython::_instances;
void SelectionObserverPythonHandler::init(const Py::Object& obj)
void SelectionObserverPythonHandler::init(PyObject* obj)
{
this->inst = obj;
#undef FC_PY_ELEMENT
#define FC_PY_ELEMENT(_name) FC_PY_GetCallable(obj.ptr(),#_name,py_##_name);
#define FC_PY_ELEMENT(_name) FC_PY_GetCallable(obj,#_name,py_##_name);
FC_PY_SEL_OBSERVER
}
SelectionObserverPythonHandler::~SelectionObserverPythonHandler()
{
#undef FC_PY_ELEMENT
#define FC_PY_ELEMENT(_name) py_##_name = Py::None();
try {
FC_PY_SEL_OBSERVER
}
catch (Py::Exception& e) {
e.clear();
}
}
void SelectionObserverPythonHandler::handleSelectionChanged(const SelectionChanges& msg)
{
switch (msg.Type)
@@ -198,7 +211,7 @@ void SelectionObserverPythonHandler::removePreselection(const SelectionChanges&
SelectionObserverPython::SelectionObserverPython(const Py::Object& obj, ResolveMode resolve)
: SelectionObserver(true, resolve)
{
this->init(obj);
this->init(obj.ptr());
}
SelectionObserverPython::~SelectionObserverPython() = default;

View File

@@ -36,7 +36,9 @@ class GuiExport SelectionObserverPythonHandler
public:
/// Constructor
explicit SelectionObserverPythonHandler() = default;
void init(const Py::Object& obj);
virtual ~SelectionObserverPythonHandler();
void init(PyObject* obj);
void handleSelectionChanged(const SelectionChanges& msg);
protected:
@@ -48,10 +50,10 @@ protected:
void removePreselection(const SelectionChanges&);
void pickedListChanged();
Py::Object inst;
private:
PyObject* inst{nullptr};
#define FC_PY_SEL_OBSERVER \
FC_PY_ELEMENT(onSelectionChanged) \
FC_PY_ELEMENT(addSelection) \
@@ -89,6 +91,7 @@ public:
private:
void onSelectionChanged(const SelectionChanges& msg) override;
Py::Object inst;
static std::vector<SelectionObserverPython*> _instances;
};

View File

@@ -74,6 +74,7 @@ class View3DInventorViewer;
class ViewProviderPy;
class ObjectItem;
class MDIView;
class SelectionChanges;
enum ViewStatus {
UpdateData = 0,
@@ -164,6 +165,8 @@ public:
/// indicates if the ViewProvider use the new Selection model
virtual bool useNewSelectionModel() const;
virtual bool isSelectable() const {return true;}
/// called when the selection changes for the view provider
virtual void onSelectionChanged(const SelectionChanges&) {}
/// return a hit element given the picked point which contains the full node path
virtual bool getElementPicked(const SoPickedPoint *, std::string &subname) const;
/// return a hit element to the selection path or 0

View File

@@ -36,6 +36,7 @@
#include <App/DocumentObjectPy.h>
#include <Base/Interpreter.h>
#include <Base/Tools.h>
#include <CXX/Python3/Objects.hxx>
#include "ViewProviderFeaturePython.h"
#include "Application.h"
@@ -44,6 +45,7 @@
#include "PythonWrapper.h"
#include "View3DInventorViewer.h"
#include "ViewProviderDocumentObjectPy.h"
#include "Selection.h"
FC_LOG_LEVEL_INIT("ViewProviderFeaturePython", true, true)
@@ -74,6 +76,8 @@ ViewProviderFeaturePythonImp::~ViewProviderFeaturePythonImp()
catch (Py::Exception& e) {
e.clear();
}
this->selectionObserver.~SelectionObserverPythonHandler();
}
void ViewProviderFeaturePythonImp::init(PyObject *pyobj) {
@@ -84,6 +88,8 @@ void ViewProviderFeaturePythonImp::init(PyObject *pyobj) {
#define FC_PY_ELEMENT(_name) FC_PY_ELEMENT_INIT(_name)
FC_PY_VIEW_OBJECT
this->selectionObserver.init(pyobj);
}
#define FC_PY_CALL_CHECK(_name) _FC_PY_CALL_CHECK(_name,return(NotImplemented))
@@ -202,6 +208,10 @@ ViewProviderFeaturePythonImp::useNewSelectionModel() const
return Accepted;
}
void ViewProviderFeaturePythonImp::onSelectionChanged(const SelectionChanges& changes) {
this->selectionObserver.handleSelectionChanged(changes);
}
bool ViewProviderFeaturePythonImp::getElement(const SoDetail *det, std::string &res) const
{
_FC_PY_CALL_CHECK(getElement,return(false));

View File

@@ -26,17 +26,19 @@
#include <App/AutoTransaction.h>
#include <App/PropertyPythonObject.h>
#include <App/FeaturePython.h>
#include <Gui/Selection/SelectionObserverPython.h>
#include "ViewProviderGeometryObject.h"
#include "Document.h"
class SoSensor;
class SoDragger;
class SoNode;
namespace Gui {
class SelectionChanges;
class GuiExport ViewProviderFeaturePythonImp
{
public:
@@ -55,6 +57,7 @@ public:
QIcon getIcon() const;
bool claimChildren(std::vector<App::DocumentObject*>&) const;
ValueT useNewSelectionModel() const;
void onSelectionChanged(const SelectionChanges&);
ValueT getElementPicked(const SoPickedPoint *pp, std::string &subname) const;
bool getElement(const SoDetail *det, std::string &) const;
bool getDetail(const char*, SoDetail *&det) const;
@@ -130,6 +133,7 @@ public:
private:
ViewProviderDocumentObject* object;
App::PropertyPythonObject &Proxy;
SelectionObserverPythonHandler selectionObserver;
bool has__object__{false};
#define FC_PY_VIEW_OBJECT \
@@ -253,6 +257,10 @@ public:
return ViewProviderT::useNewSelectionModel();
}
}
/// called when the selection changes for the view provider
void onSelectionChanged(const SelectionChanges& changes) override {
return imp->onSelectionChanged(changes);
}
bool getElementPicked(const SoPickedPoint *pp, std::string &subname) const override {
auto ret = imp->getElementPicked(pp,subname);
if(ret == ViewProviderFeaturePythonImp::NotImplemented)