[Solved]Help me understand Selection Observer

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
alberich
Posts: 77
Joined: Thu Aug 17, 2017 2:09 pm
Location: Texas

[Solved]Help me understand Selection Observer

Post by alberich »

I'm trying to build a macro that analyzes selected edges of a body and sends the analysis result on to Draft and then Techdraw. I've got all the bits and pieces working but the edge selecting process bothers me because, although it works, I don't understand how it works. Here's the relevent chunk of code:

Code: Select all

"""Stripped down from MyDialog.FCMacro by openBrain

      https://github.com/0penBrain/FreeCAD-macros/blob/MyDialog/Examples/MyDialog.FCMacro

   Demonstrate the selection  and reporting of edges.

"""
import re

from PySide2 import QtCore, QtWidgets
import FreeCADGui as Gui

MSG = App.Console.PrintMessage

class MyDialog(QtWidgets.QDialog):

    class MySelObs(QtCore.QObject):
        """Selection Observer"""

        selection = QtCore.Signal(str)
        clear = QtCore.Signal(str)

        def __init__(self):
            super().__init__()

        def addSelection(self, doc, obj, sub, pnt):
            print(sub, sub[:4])
            if sub[:4] == "Edge":  # Is sub-element an Edge? Check head of string.
                x = sub[4:]  # get numerical value of sub-element from tail of string.
                self.selection.emit(f'Edge {x[0]}')
            else:
                print("\nNot an Edge. Deselect and try again.\n")

        def clearSelection(self,doc):
            self.clear.emit(f'Selection cleared in document {doc}')


    def __init__(self, parent=Gui.getMainWindow()):
        super().__init__(parent, QtCore.Qt.Tool)
        self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
        self.setAttribute(QtCore.Qt.WA_WindowPropagation, True)
        self.initUI()


        if Gui.activeView() and repr(Gui.activeView()) == 'View3DInventor':
            self.view = Gui.activeView()
        else:
            pass  # Only running this in Gui active anyway

        self.sel_observer = self.MySelObs()
        self.sel_observer.selection.connect(self.writeSel)
        self.sel_observer.clear.connect(self.writeSel)
        Gui.Selection.addObserver(self.sel_observer)

    def initUI(self):
        self.setWindowTitle('Edge Gatherer Test')

        my_lay = QtWidgets.QGridLayout(self)
        self.setLayout(my_lay)

        my_sel_ob_w = QtWidgets.QWidget(self)
        my_lay.addWidget(my_sel_ob_w, 0, 1)
        my_button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel, self)
        my_lay.addWidget(my_button_box, 2, 0, 1, -1)


        my_sel_ob_lay = QtWidgets.QVBoxLayout(my_sel_ob_w)
        my_sel_ob_w.setLayout(my_sel_ob_lay)
        my_sel_ob_lay.addWidget(QtWidgets.QLabel('Selection observer:', my_sel_ob_w))
        self.my_sel_ob = QtWidgets.QPlainTextEdit(my_sel_ob_w)
        self.my_sel_ob.setReadOnly(True)
        self.my_sel_ob.setMaximumBlockCount(100)
        my_sel_ob_lay.addWidget(self.my_sel_ob)


        my_button_box.accepted.connect(self.accept)
        my_button_box.rejected.connect(self.reject)


    def writeSel(self, text):
        self.my_sel_ob.appendPlainText(text)

    def accept(self):
        MSG('Good bye\n')
        super().accept()

    def reject(self):
        print('\nSorry.  [Cancel] doesn't work yet.\n')

    def hideEvent(self, event):
        if hasattr(self, 'view_callback'):
            try:
                self.view.removeEventCallback('SoEvent', self.view_callback)
            except RuntimeError:
                pass
        Gui.Selection.removeObserver(self.sel_observer)
        super().hideEvent(event)


MyDialog().show()
Where my understanding has a gap is at the line

Code: Select all

def addSelection(self, doc, obj, sub, pnt):
How do the parameters here get populated? I know what they are but I don't see how they get here. I can also see that they are the same parameters I see when mousing over a body, reported as

Code: Select all

"Preselected: <doc>.<obj>.<sub> (pnt)

and also in the python console when clicked on as

Code: Select all

# Gui.Selection.addSelection(doc,obj,sub,pnt.x,pnt.y,pnt.z)
Does it tie together here?

Code: Select all

        self.sel_observer = self.MySelObs()  # <<<<------<<<<
        self.sel_observer.selection.connect(self.writeSel)
        self.sel_observer.clear.connect(self.writeSel)
        Gui.Selection.addObserver(self.sel_observer)  # <<<<------<<<<
Edit: added credit in code for openBrain, removed misleading comment in code.
Last edited by alberich on Tue Apr 20, 2021 1:04 pm, edited 2 times in total.
User avatar
Chris_G
Veteran
Posts: 2598
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Help me understand Selection Observer

Post by Chris_G »

In your code, you have this line at the end of the __init__ of class MyDialog :

Code: Select all

Gui.Selection.addObserver(self.sel_observer)
This adds self.sel_observer to the list of objects to which the Selection mechanism of FreeCAD Gui will transmit its information.
As such, these objects are supposed to have defined special methods that will be called by Gui.Selection.
They are addSelection(), clearSelection() and so on...
alberich
Posts: 77
Joined: Thu Aug 17, 2017 2:09 pm
Location: Texas

Re: Help me understand Selection Observer

Post by alberich »

So Gui.Selection.addObserver connects me to the Selection API.

When I look on the wiki https://wiki.freecadweb.org/Selection_API I see

Code: Select all

addSelection(FreeCAD.Object)
Is that addSelection my addSelection? So that in my

Code: Select all

addSelection(self, doc, obj, sub, pnt)
the parameters self,doc,obj,sub,pnt is the API's FreeCAD.Object?
User avatar
Chris_G
Veteran
Posts: 2598
Joined: Tue Dec 31, 2013 4:10 pm
Location: France
Contact:

Re: Help me understand Selection Observer

Post by Chris_G »

alberich wrote: Mon Apr 19, 2021 1:13 pm Is that addSelection my addSelection? So that in my
This addSelection() is a method of FreeCADGui.Selection module :

Code: Select all

FreeCADGui.Selection.addSelection(a_FreeCAD_object)
It is also possible to specify subNames :

Code: Select all

FreeCADGui.Selection.addSelection(a_FreeCAD_object, ["Face6", "Face8"])
Whenever FreeCADGui.Selection receives a selection action (either by the above code, by a mouse action in the Gui, in the Tree-View, or by a click on a subshape, in the 3D View), it loops over its list of Observer objects, and call their own functions corresponding to the action (addSelection, clearSelection, removeSelection).
alberich
Posts: 77
Joined: Thu Aug 17, 2017 2:09 pm
Location: Texas

Re: Help me understand Selection Observer

Post by alberich »

Ok. I think I've got a bit better handle on this.

By rummaging in the source code, first at Sourceforge then GitHub, I found in Selection.h there is

Code: Select all

struct SelObj {
        const char* DocName;
        const char* FeatName;
        const char* SubName;
        const char* TypeName;
        App::Document* pDoc;
        App::DocumentObject*  pObject;
        App::DocumentObject* pResolvedObject;
        float x,y,z;
};
that has the bits I'm trying to track. I surmise that the python Selection API handles
passing this struct (or parts of it) around.

In my example, the line

Code: Select all

Gui.Selection.addObserver(self.sel_observer) 

installs an instance of my class MySelObj as an Observer that is privy to the struct elements
managed by the underlying C++ library. It is through this installation that my method addSelection
becomes aware of the Gui.Selection.AddSelection method and can, thus, share the contents of that C++ struct.
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: Help me understand Selection Observer

Post by Kunda1 »

In your rummaging, Please consider adding some source code comments aka doxygen documentation as it is sorely missing
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
openBrain
Veteran
Posts: 9041
Joined: Fri Nov 09, 2018 5:38 pm
Contact:

Re: Help me understand Selection Observer

Post by openBrain »

Kunda1 wrote: Tue Apr 20, 2021 12:24 am In your rummaging, Please consider adding some source code comments aka doxygen documentation as it is sorely missing
OP copied my tutorial code before I added the comments. :lol:

@alberich, maybe it would be good to tell that you get it working by copying and adapting existing code. Not that I care you took my code (that's the purpose, and I'm glad it serves), but it may help forumers understand why you can get a bit lost with it. ;)
openBrain
Veteran
Posts: 9041
Joined: Fri Nov 09, 2018 5:38 pm
Contact:

Re: Help me understand Selection Observer

Post by openBrain »

alberich wrote: Mon Apr 19, 2021 9:39 pm Ok. I think I've got a bit better handle on this.

By rummaging in the source code, first at Sourceforge then GitHub, I found in Selection.h there is
Available methods for Selection observer are listed in Selection.h, but care to look at the SelectionObserver class : https://github.com/FreeCAD/FreeCAD/blob ... #L276-L283
alberich
Posts: 77
Joined: Thu Aug 17, 2017 2:09 pm
Location: Texas

Re: Help me understand Selection Observer

Post by alberich »

openBrain wrote: Tue Apr 20, 2021 5:32 am OP copied my tutorial code before I added the comments. :lol:

@alberich, maybe it would be good to tell that you get it working by copying and adapting existing code. Not that I care you took my code (that's the purpose, and I'm glad it serves), but it may help forumers understand why you can get a bit lost with it. ;)
@openBrain, I'm glad you popped in. I wanted to credit your help in OP but honestly couldn't find my way back to where I originally found the code. I have it saved as SampleDialog.FCMacro but I've been DGGing for an hour and can't find a link back to its origin. I also had in mind that it was from a different FCer :oops:

If you can give me a link I'll edit the OP.

Cancel that. I just found it. It's much changed from when I grabbed it before. (New name, hence the failed search?) The new comments are clarifying.

The link is here

Code: Select all

https://github.com/0penBrain/FreeCAD-macros/blob/MyDialog/Examples/MyDialog.FCMacro
I'll edit the OP to add it there as well.

As an aside, I did also find FreeCAD_Mod_Dev_Guide/chapters at master · qingfengxia/FreeCAD_Mod_Dev_Guide at https://github.com/qingfengxia/FreeCAD_ ... r/chapters. While not up to date with current FC development it was a great one-stop shopping help for a FC code overview.
alberich
Posts: 77
Joined: Thu Aug 17, 2017 2:09 pm
Location: Texas

Re: Help me understand Selection Observer

Post by alberich »

Kunda1 wrote: Tue Apr 20, 2021 12:24 am In your rummaging, Please consider adding some source code comments aka doxygen documentation as it is sorely missing
@Kunda1, I'd be more than happy to do that but my tenuous grasp on the technicalities wouldn't support it. I'd be more qualified to edit for grammar.
Post Reply