Enumerate Property

Need help, or want to share a macro? Post here!
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
keithsloan52
Veteran
Posts: 2755
Joined: Mon Feb 27, 2012 5:31 pm

Enumerate Property

Post by keithsloan52 »

I have an enumerate property which can have about 50-60 different values, ideally I would like to split the list into sub lists.
i.e. When a user selects such a property, instead of being offered a list of 50-60 values they are offered say 5 and depending on which one
of the five is selected a list of around 10 values are offered.

Is this possible? or do I need to create a feature request?
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Enumerate Property

Post by TheMarkster »

Possibly you could populate it based on the selection with a "back" option at the top to get back to the previous level.

Or you could have 2 enumerate properties and populate the 2nd one based on the selection in the first.
keithsloan52
Veteran
Posts: 2755
Joined: Mon Feb 27, 2012 5:31 pm

Re: Enumerate Property

Post by keithsloan52 »

TheMarkster wrote: Tue Jan 25, 2022 1:19 am Possibly you could populate it based on the selection with a "back" option at the top to get back to the previous level.

Or you could have 2 enumerate properties and populate the 2nd one based on the selection in the first.
But how do you code it?

You add Objects properties with calls like obj.addProperty("App::PropertyFloat", "Length")
and enumeration is one property type https://wiki.freecadweb.org/Property

But I don't know of any way to control how FreeCAD handles a Property, like there is no call back function that I am aware of.
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Enumerate Property

Post by TheMarkster »

I'm thinking you have the top level items in the enumeration to start with:

Code: Select all

obj.addProperty("App::PropertyEnumeration", "Enum").Enum = ["A", "B", "C"]

def onChanged(self, fp, prop):
    if prop == "Enum":
        if fp.Enum == "A":
            fp.Enum = ["Back", "Apple", "Artichoke"]
        elif fp.Enum == "B":
            fp.Enum = ["Back", "Banana", "Belgium Waffle"]
        elif fp.Enum == "C":
            fp.Enum = ["Back", "Cranberries", "Custard", "Cucumbers"]
        elif fp.Enum == "Back":
            fp.Enum = ["A", "B", "C"]
        else:
            self.handleEnum(fp, prop) #in here you would test for the various menu options, all of which should be unique
            #or you might deal with the Enum value in execute(self, fp) instead.
            
All of this is untested. It might cause some infinite loop or some other problem to change the enumeration during onChanged(). If that happens you might be able to set some flag such as bHandlingEnumeration and set it to True when changing the enumeration.

Code: Select all

    if prop == "Enum" and not self.bHandlingEnumeration:
        if fp.Enum == "A":
            self.bHandlingEnumeration = True
            fp.Enum = ["Back", "Apple", "Artichoke"]
            self.bHandlingEnumeration = False
keithsloan52
Veteran
Posts: 2755
Joined: Mon Feb 27, 2012 5:31 pm

Re: Enumerate Property

Post by keithsloan52 »

TheMarkster wrote: Tue Jan 25, 2022 6:52 pm I'm thinking you have the top level items in the enumeration to start with:

Code: Select all

obj.addProperty("App::PropertyEnumeration", "Enum").Enum = ["A", "B", "C"]

def onChanged(self, fp, prop):
    if prop == "Enum":
        if fp.Enum == "A":
            fp.Enum = ["Back", "Apple", "Artichoke"]
        elif fp.Enum == "B":
            fp.Enum = ["Back", "Banana", "Belgium Waffle"]
        elif fp.Enum == "C":
            fp.Enum = ["Back", "Cranberries", "Custard", "Cucumbers"]
        elif fp.Enum == "Back":
            fp.Enum = ["A", "B", "C"]
        else:
            self.handleEnum(fp, prop) #in here you would test for the various menu options, all of which should be unique
            #or you might deal with the Enum value in execute(self, fp) instead.
            
All of this is untested. It might cause some infinite loop or some other problem to change the enumeration during onChanged(). If that happens you might be able to set some flag such as bHandlingEnumeration and set it to True when changing the enumeration.

Code: Select all

    if prop == "Enum" and not self.bHandlingEnumeration:
        if fp.Enum == "A":
            self.bHandlingEnumeration = True
            fp.Enum = ["Back", "Apple", "Artichoke"]
            self.bHandlingEnumeration = False
No loop but when I select B it just displays the letter B and not 'Back' etc.
The value has changed as a subsequent change gives

20:51:17 <Exception> 'C' is not part of the enumeration

Needs some call to refresh the property window
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Enumerate Property

Post by TheMarkster »

This works somewhat, but you need to click away and click back to see the updated items.

Code: Select all

class Enums:
    def __init__(self,obj):
        obj.addProperty("App::PropertyEnumeration","Enum").Enum=["Select One","A","B","C"]
        obj.Proxy = self
        self.bIsHandlingEnumeration = False

    def onChanged(self, fp, prop):
        if prop == "Enum" and not self.bIsHandlingEnumeration:
            self.bIsHandlingEnumeration = True
            current = getattr(fp,"Enum")
            if current == "A":
                fp.Enum = ["Back", "Apple", "Apricot"]
                fp.Enum = "Apple"
                self.bIsHandlingEnumeration = False
            elif current == "B":
                fp.Enum = ["Back", "Banana", "Blackberry"]
                fp.Enum = "Banana"
                self.bIsHandlingEnumeration = False
            elif current == "C":
                fp.Enum = ["Back", "Carrots", "Cabbage", "Cinnamon Rolls"]
                fp.Enum = "Carrots"
                self.bIsHandlingEnumeration = False
            elif current == "Back":
                fp.Enum = ["Select One","A","B","C"]
                self.bIsHandlingEnumeration = False
            else:
                self.bIsHandlingEnumeration = False

enum = FreeCAD.ActiveDocument.addObject("App::FeaturePython","FP")
Enums(enum)
FreeCAD.ActiveDocument.recompute()
keithsloan52
Veteran
Posts: 2755
Joined: Mon Feb 27, 2012 5:31 pm

Re: Enumerate Property

Post by keithsloan52 »

TheMarkster wrote: Wed Jan 26, 2022 12:11 am This works somewhat, but you need to click away and click back to see the updated items.
Mmmh Close but no cigar
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Enumerate Property

Post by TheMarkster »

It could be possible with Qt to set the focus away and then back to the tree widget to make the update happen, maybe with a singleShot a few milliseconds after finishing in onChanged(). I also didn't try Gui.updateGui(). There might be a way to force a property update before it loses focus, too, but I don't know it. Another option could be to have 2 separate Enums, one with the top level "A, B, C" options and the other with the lower level "Apple, Artichoke" options. Or just put all the options in a single enumeration, perhaps with some leading spaces to distinguish the sublevels.
keithsloan52
Veteran
Posts: 2755
Joined: Mon Feb 27, 2012 5:31 pm

Re: Enumerate Property

Post by keithsloan52 »

TheMarkster wrote: Wed Jan 26, 2022 4:58 pm It could be possible with Qt to set the focus away and then back to the tree widget to make the update happen, maybe with a singleShot a few milliseconds after finishing in onChanged(). I also didn't try Gui.updateGui(). There might be a way to force a property update before it loses focus, too, but I don't know it. Another option could be to have 2 separate Enums, one with the top level "A, B, C" options and the other with the lower level "Apple, Artichoke" options. Or just put all the options in a single enumeration, perhaps with some leading spaces to distinguish the sublevels.
  • Tried Gui.updateGui()
    - did not work, change anything.
  • single enumeration, perhaps with some leading spaces to distinguish the sublevels.
    - Does not address the problem that the list is too long to scroll through
TheMarkster
Veteran
Posts: 5505
Joined: Thu Apr 05, 2018 1:53 am

Re: Enumerate Property

Post by TheMarkster »

This hack sets the focus to the report view and back to the tree view in a singleShot()

Code: Select all

from PySide import QtCore, QtGui
class Enums:
    def __init__(self,obj):
        obj.addProperty("App::PropertyEnumeration","Enum").Enum=["Select One","A","B","C"]
        obj.Proxy = self
        self.bIsHandlingEnumeration = False

    def onChanged(self, fp, prop):
        if prop == "Enum" and not self.bIsHandlingEnumeration:
            self.bIsHandlingEnumeration = True
            current = getattr(fp,"Enum")
            if current == "A":
                fp.Enum = ["Back", "Apple", "Apricot"]
                fp.Enum = "Apple"
                self.bIsHandlingEnumeration = False
            elif current == "B":
                fp.Enum = ["Back", "Banana", "Blackberry"]
                fp.Enum = "Banana"
                self.bIsHandlingEnumeration = False
            elif current == "C":
                fp.Enum = ["Back", "Carrots", "Cabbage", "Cinnamon Rolls"]
                fp.Enum = "Carrots"
                self.bIsHandlingEnumeration = False
            elif current == "Back":
                fp.Enum = ["Select One","A","B","C"]
                self.bIsHandlingEnumeration = False
            else:
                self.bIsHandlingEnumeration = False
            t = QtCore.QTimer()
            t.singleShot(50, self.focusTree)

    def focusTree(self):
        rv = Gui.getMainWindow().findChild(QtGui.QTextEdit, "Report view")
        trees = Gui.getMainWindow().findChildren(QtGui.QTreeWidget)
        if trees:
            tree = trees[0]
            rv.setFocus()
            tree.setFocus()

enum = FreeCAD.ActiveDocument.addObject("App::FeaturePython","FP")
Enums(enum)
FreeCAD.ActiveDocument.recompute()
Post Reply