[Solved] PyQt with Selection Observer

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
thyssentishman
Posts: 82
Joined: Mon May 16, 2022 10:35 am

[Solved] PyQt with Selection Observer

Post by thyssentishman »

Hi all,

I would like to know how I can implement a Selection Observer like the one from here into my Qt interface so that the user can select edges on the 3D view and add them to a QListWidget. I've seen this functionality in many tools from FreeCAD. Here's what I have so far:

Code: Select all

import FreeCAD
from PySide import QtGui, QtCore

class SelObserver:

    def addSelection(self):
        sel = FreeCADGui.Selection.getSelectionEx()[0].SubElementNames[0]
        
        # ...
        # return selected edge (sel) so it can be added to list:
        # self.edges_list.addItem(sel)
        # ...

        FreeCADGui.Selection.removeObserver(s)

s = SelObserver()

class GuiDialog(QtGui.QDialog):

    def __init__(self):
        super(GuiDialog, self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle(" ")
        self.setFixedSize(175,215)
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        
        self.add_edge = QtGui.QPushButton('Add Edge', self)
        self.edges_list_label = QtGui.QLabel("Edges:", self)
        self.edges_list = QtGui.QListWidget(self)
        self.cancel = QtGui.QPushButton('Cancel', self)

        self.add_edge.clicked.connect(self.addObject)
        self.cancel.clicked.connect(self.onCancel)
    
        self.add_edge.move(10,10)
        self.add_edge.resize(150, 25)
        self.edges_list_label.move(10,45)
        self.edges_list.move(10,60)
        self.edges_list.resize(155,110)
        self.cancel.move(90,180)

        self.show()

    def onCancel(self):
        self.close()

    def addObject(self, sel):
        FreeCADGui.Selection.addObserver(s)


gui = GuiDialog()
gui.exec_()

As an example of how it should work, the Additive Pipe tool in the Part Design WB has an "Add Edge" button for the user to select an Edge and add it to the list below the button.

OS: Windows 10 Version 2009
Word size of OS: 64-bit
Word size of FreeCAD: 64-bit
Version: 2022.402.28566 (Git)
Build type: Release
Branch: LinkStage3
Hash: 0da0525ba7ff10c9efe0476d35ffcdf040ed470d
Python version: 3.8.6+
Qt version: 5.15.2
Coin version: 4.0.1
OCC version: 7.5.0
Locale: English/Germany (en_DE)
Last edited by thyssentishman on Fri May 20, 2022 11:50 am, edited 2 times in total.
mario52
Veteran
Posts: 4673
Joined: Wed May 16, 2012 2:13 pm

Re: PyQt with Selection Observer

Post by mario52 »

Hi

without selObserver

Code: Select all

import FreeCAD
from PySide import QtGui, QtCore

#class SelObserver:
#
#    def addSelection(self):
#        sel = FreeCADGui.Selection.getSelectionEx()[0].SubElementNames[0]
#        
#        # ...
#        # return selected edge (sel) so it can be added to list:
#        # self.edges_list.addItem(sel)
#        # ...
#
#        FreeCADGui.Selection.removeObserver(s)
#
#s = SelObserver()
#
class GuiDialog(QtGui.QDialog):

    def __init__(self):
        super(GuiDialog, self).__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle(" ")
        self.setFixedSize(175,280)
        self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
        
        self.add_edge = QtGui.QPushButton('Add Edge', self)
        self.edges_list_label = QtGui.QLabel("Edges:", self)
        self.edges_list = QtGui.QListWidget(self)

        self.cancel = QtGui.QPushButton('Cancel', self)
        self.cancel.setIcon(QtGui.QIcon.fromTheme("cancel",QtGui.QIcon(":/icons/edit_Cancel.svg")))
        self.del_edge = QtGui.QPushButton('Del item', self)
        self.clear_list = QtGui.QPushButton('Clear', self)

        self.add_edge.clicked.connect(self.addObject)
        self.del_edge.clicked.connect(self.delEdge)
        self.clear_list.clicked.connect(self.clearList)
        self.cancel.clicked.connect(self.onCancel)
    
        self.add_edge.move(10,10)
        self.add_edge.resize(150, 25)

        self.del_edge.move(10,40)
        self.del_edge.resize(150, 25)
        self.clear_list.move(10,70)
        self.clear_list.resize(150, 25)

        self.edges_list_label.move(10,100)
        self.edges_list.move(10,120)
        self.edges_list.resize(155,110)

        self.cancel.move(90,240)

        self.show()

    def addObject(self, sel):
        #sel = FreeCADGui.Selection.getSelectionEx()[0].SubObjects[0] 
        sel = FreeCADGui.Selection.getSelectionEx()[0]
        subName = sel.SubElementNames[0]
        self.edges_list.addItem(subName)

    def clearList(self):
        self.edges_list.clear()    # clear the window

    def delEdge(self):
        self.edges_list.takeItem(self.edges_list.currentRow()) # del the item selected

    def onCancel(self):
        self.close()

gui = GuiDialog()
gui.exec_()
mario
Maybe you need a special feature, go into Macros_recipes and Code_snippets, Topological_data_scripting.
My macros on Gist.github here complete macros Wiki and forum.
thyssentishman
Posts: 82
Joined: Mon May 16, 2022 10:35 am

Re: PyQt with Selection Observer

Post by thyssentishman »

Hi @mario52,

Thank you so much. This definitely achieves what I wanted. I don't know how I didn't think of that. I guess I thought of first selecting the "Add Edge" button and then making the selection, but it's obviously simpler the other way around.

I will mark this post as solved, however I would still like to know how to implement the Selection Observer with a Qt interface. For example if I wanted the user to select a set of points in a face (a good example of this is the tool CurveOnMesh from the Surface WB). I believe that to achieve this kind of functionality, a Selection Observer is needed so that the user can make multiple selections before performing an action on them.

Could you maybe provide me with some guidance here. I believe the code I sent you previously could easily be adapted for this functionality.

Thank you!
openBrain
Veteran
Posts: 9034
Joined: Fri Nov 09, 2018 5:38 pm
Contact:

Re: PyQt with Selection Observer

Post by openBrain »

rvq wrote: Fri May 20, 2022 7:10 am Could you maybe provide me with some guidance here. I believe the code I sent you previously could easily be adapted for this functionality.

Thank you!
Here is a minimal demonstrating macro :

Code: Select all

from PySide2 import QtCore, QtWidgets
import FreeCADGui as Gui

class myWid(QtWidgets.QListWidget):

	def __init__(self,parent):
		super().__init__(parent)
		self.setWindowFlag(QtCore.Qt.Tool)
		self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True)
		self.setAttribute(QtCore.Qt.WA_WindowPropagation, True)
		Gui.Selection.addObserver(self)

	def addSelection(self,doc,obj,sub,pnt):
		self.updateView()

	def clearSelection(self,doc):
		self.updateView()

	def removeSelection(self,doc,obj, sub):
		self.updateView()

	def updateView(self):
		self.clear()
		sel = Gui.Selection.getSelectionEx('',0)
		for o in sel:
			for so in o.SubElementNames:
				self.addItem(so)

	def closeEvent(self, event):
		Gui.Selection.removeObserver(self)

wid = myWid(Gui.getMainWindow())
wid.show()
thyssentishman
Posts: 82
Joined: Mon May 16, 2022 10:35 am

Re: [Solved] PyQt with Selection Observer

Post by thyssentishman »

Thank you @openBrain, this helps me a lot!
Post Reply