Save ramp presets

   2127   10   3
User Avatar
Member
385 posts
Joined: July 2018
Offline
How can i save ramps presets without using the "save preset" button from the gear icon?

i have an hda with lots of parameters and i don't want to create a new preset for every ramp i want to save.
Ideal interface would be a drop down menu and a save button next to my ramp parameter.

Could i export the ramp as a json file in my HIP dir and append to that each time i press save? then load the selected ramp from the drop down menu somehow (menu would be populated by the json file).
i am thinking python module but i am not sure about this

if writing the ramps in an external file is the way to go, how would i do that and then read the data back?

Any tips on the workflow would be much appreciated!
User Avatar
Member
385 posts
Joined: July 2018
Offline
baby step 1

made a dictionary of the ramp parameters

import json

node = kwargs['node']
ramp = node.parm('ramp').multiParmInstances()
num = node.parm('ramp').multiParmInstancesPerItem()

dict = {
    'point': []
}

for p in ramp:
    data = {}
    for i in range(num): 
        if str(i+1) in p.name():
            value = p.eval()
            name = p.name()
           
            data[name] = value 
    dict['point'].append(data)
            
print(dict)

next step is to make a correct json structure (one entry per ramp point holding pos val and interpolation values) and save it to disk
User Avatar
Member
385 posts
Joined: July 2018
Offline
baby step 2
corrected the json structure

import json

node = kwargs['node']
ramp = node.parm('ramp').multiParmInstances()
num = node.parm('ramp').multiParmInstancesPerItem()

dict = {
    'point': []
}

for i in range(num): 
    data={}
    for p in ramp:
        if str(i+1) in p.name():
            value = p.eval()
            name = p.name()
        
            data[name] = value 
            
    dict['point'].append(data)   
    
print(dict)

next step is to append different ramp presets to the same json
and the step after that is to load them
User Avatar
Member
385 posts
Joined: July 2018
Offline
baby step 3
append or overwrite options with pop up window
import json
import os

path = hou.hipFile.path().rsplit('/',1)[0] + '/ramp_presets/'
if not os.path.exists(path):
    os.makedirs(path)

node=kwargs['node']
parm = node.parm('ramp')
ramp = parm.evalAsRamp()
interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]

keylist = []
for i,key in enumerate(ramp.keys()):
    data = { "pos": i,
             "value": ramp.values()[i],
             "interp": interplookup.index(ramp.basis()[i])}    
    keylist.append(data)

dict = { 'ramp_name': keylist }

filepath = path + 'test.json'
json_data = json.dumps(dict, indent=4)

if os.path.isfile(filepath) and os.path.getsize(filepath)>0: #if file exists and is not zero bytes
    user = hou.ui.displayMessage('a file already exists',buttons=('Append','Overwrite',)) 
    
    if user==0:
        with open(filepath,'a') as f:
            f.write(json_data)
    if user==1:
        os.remove(filepath)
        with open(filepath,'w') as f:
            f.write(json_data)       
else:
    with open(filepath,'w') as f:
        f.write(json_data)

by the way a different approach to constructing the dictionary was given by tamte aka Tomas Slancik
Edited by papsphilip - Oct. 13, 2021 15:24:50
User Avatar
Member
385 posts
Joined: July 2018
Offline
after lots of baby steps...
ok i finished the HDA for saving ramp presets and loading them back in.

Attachments:
FP.dev.ramp.1.2.hda (10.9 KB)

User Avatar
Member
1 posts
Joined: Dec. 2018
Offline
can we change ramps using a slider is that possible if we have same no. of points in ramp
User Avatar
Member
385 posts
Joined: July 2018
Offline
Do you mean a slider for each point on the ramp?
User Avatar
Member
131 posts
Joined: Aug. 2012
Offline
Saman_batham
can we change ramps using a slider is that possible if we have same no. of points in ramp

Ramp parameters are essentially multiparms - you can reference each point's position/value parameter directly if you read the "index" value. If you have a ramp parameter with a fixed number of points, you can channel-reference each point's Value independently.
User Avatar
Member
33 posts
Joined: Feb. 2016
Offline
papsphilip
after lots of baby steps...
ok i finished the HDA for saving ramp presets and loading them back in.

Thanks a bunch for this, but I get an error message for folder where I have read/write rights:

Traceback (most recent call last):
File "FP::dev::Sop/ramp::1.2/save", line 1, in <module>
File "FP::dev::Sop/ramp::1.2, PythonModule", line 57, in Save
PermissionError: [ErrnoTraceback (most recent call last):
File "

And is it also possible to save RGB ramps?

Thanks in advance!
Edited by Yader - Sept. 9, 2022 05:21:14
https://behance.net/derya [behance.net]
User Avatar
Member
7 posts
Joined: Nov. 2020
Online
Thanks @papsphilip and @tamte for this solution! For an HDA with many parameters I wanted to export all parameters including color and float ramps as well as multiparms, so I extended the script a bit. Now it seems like all standard parameter types are supported. While ramps are detected automatically, for now multiparms need to be manually defined in the script. Does anyone know a way to isolate multiparms in Python?

### Global

import json, os

# Manually Define Multiparms Here
mplist = ['mp_a', 'mp_b', 'mp_c']

# Wrappers for Separating Ramp, Standard and Multiparm Data in the .json File
stdgroup = 'standard'
rampgroup = 'ramps'
mpgroup = 'multiparms'

### Export Parameters

def write_params(kwargs):
        
    hda = kwargs['node']
    params = hda.parmsInFolder(['Parameters'])

    from sys import platform
    
    dir = hda.parm('path').evalAsString() + '/cache/'
    # Windows and MacOS Handle File Paths Slightly Different
    if platform == 'darwin':
        dir = hda.parm('path').evalAsString() + 'cache/'
    
    filepath = dir + '_config_' + hda.parm('preset').evalAsString() + '.json'
    
    data = {}
    ramp = {}
    ramps = {}
    mp = {}

    for parm in params:
        if not parm.isHidden():
  
            # Loop Through Ramp Parameters
            if parm.isMultiParmParent():
            
                rampname = parm.name()
                ramp = parm.evalAsRamp()
                interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
    
                keylist = []
                for i,key in enumerate(ramp.keys()):
                    rampdata = { 'posindex': i,
                        'pos' : key,   
                        'value': ramp.values()[i],
                        'interp': interplookup.index(ramp.basis()[i])}    
                    keylist.append(rampdata)

                ramp = { rampname: keylist }
                ramps.update(ramp)
                
                # <continue> ends the for loop’s current iteration
                continue

            # Write Standard Parameters
            data[parm.name()] = parm.eval()

    # Loop Through Multiparms
    for mpi in mplist:
        mparam = hda.parm(mpi)

        mp_inst = {
            mpi: []
        }
        
        num = hda.parm(mpi).evalAsInt()
        instances = hda.parm(mpi).multiParmInstances()

        for i in range(num):
    
            block_data = {}
            for instance in instances:
                if instance.name().find(str(i+1)) >=0:
                    name = instance.name()
                    value = instance.eval()
                    block_data[name] = value
            mp_inst[mpi].append(block_data)
            mp.update(mp_inst)
            #print('........\n')

    # Wrapper for Standard Parameters
    datas = { stdgroup: data }

    # Wrapper for Ramp Parameters
    ramps = { rampgroup: ramps } 

    # Wrapper for Multiparms
    mps = { mpgroup: mp }

    # Append Ramp Values to the Final Dictionary
    datas.update(ramps)

    # Append the Mltiparm Values to the Final Dictionary
    datas.update(mps)
            
    if not os.path.exists(dir):
        os.makedirs(dir)
            
    with open(filepath, 'w') as outfile:
        json.dump(datas, outfile, indent=4)

    print('Parameters exported to:', filepath)
        
### Import Parameters

def read_params(kwargs):
    
    hda = kwargs['node']
    params = hda.parmsInFolder(['Parameters'])
    
    from sys import platform
    
    dir = hda.parm('path').evalAsString() + '/cache/'
    if platform == 'darwin':
        dir = hda.parm('path').evalAsString() + 'cache/'
        
    filepath = dir + '_config_' + hda.parm('preset').evalAsString() + '.json'
        
    if len(filepath) > 0 and os.path.isfile(filepath):
        # Read JSON for Standard Parameters
        with open(filepath, 'r') as outfile:
            parameters = json.load(outfile)[stdgroup]

        # Read JSON for Ramp Parameters
        with open(filepath, 'r') as outfile:
            rparameters = json.load(outfile)[rampgroup]

        # Read JSON for Multiparms
        with open(filepath, 'r') as outfile:
            mparameters = json.load(outfile)[mpgroup]

        # Set Standard Parameters
        for parm_name, value in parameters.items():
            parm = hda.parm(parm_name)
            if parm is not None:
                parm.set(value)

        # Set Ramp Parameters
        # Outer Loop Runs Over Every Ramp Parameter Name
        for parm_name, value in rparameters.items():
            parm = hda.parm(parm_name)

            if parm is not None:
                keys = []
                values = []
                bases = []
                interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
                # Inner Loop - We Query the Current Ramp Name With <[parm_name]>
                for i in rparameters[parm_name]:
                    keys.append(i['pos'])
                    values.append(i['value'])
                    bases.append(interplookup[i['interp']])

                # Initialize New Ramp
                ramp = hou.Ramp(bases, keys, values)
                # Set Ramp Parameters
                parm.set(ramp)

        # Set Multiparms
        # Outer Loop Runs Over Every Multiparm Name
        for parm_name, value in mparameters.items():
            parm = hda.parm(parm_name)
            parm.set(0)

            if len(mparameters) > 0:
                blocks = mparameters[parm_name]

                for i in range(len(blocks)):
                    parm.insertMultiParmInstance(i)
                
                    for name, value in blocks[i].items():
                        hda.parm(name).set(value)
                
        print('Parameters imported from:', filepath)
        
    else:
        hou.ui.displayMessage('Couldnt find the file!!')

Attachments:
FF_JSON_Export.hipnc (140.8 KB)

User Avatar
Member
7 posts
Joined: Nov. 2020
Online
Nevermind, I found a solution. Now all parameters including multiparms, float and color ramps (also inside subfolders and multiparms) can be written to and loaded from the .json file automatically. A problem with digits in Multiparm names and numbers > 10 is also fixed.

### JSON Export Script

### Usage

# All Parameters within the folder <parameters> can be written to a .json file with the <Save Config> button.
# They can be loaded back as a preset with the <Load Config> button.
# By default the .json files are saved in a folder named <cache> within the current $HIP directory (can be changed in <Home Directory>).
# There is a dropdown menu with numbered preset options. Every preset corresponds to a separate .json file.
# Standard parameter types are supported including Color and Float Ramps.
# Multiparms and Ramps can be nested in subfolders.

### Limitations

# Multiparms cannot be nested within multiparms.
# Names of multiparm attributes need to end with the number sign #
# Names of multiparm attributes can contain digits, but not directly before the number sign # (e.g: float3_# works, float3# doesn't).

##############
### Global ###
##############

import json, os

# Groups for Separating and Wrapping Ramp, Standard and Multiparm Data in the .json File
stdgroup = 'standard'
rampgroup = 'ramps'
mpgroup = 'multiparms'
mprampgroup = 'mpramp'

#########################
### Export Parameters ###
#########################

def write_params(kwargs):
        
    hda = kwargs['node']
    params = hda.parmsInFolder(['Parameters'])
    #print('........\n', params)

    from sys import platform
    
    dir = hda.parm('path').evalAsString() + '/cache/'
    if platform == 'darwin': # Considers Windows / MacOS File Path Difference 
        dir = hda.parm('path').evalAsString() + 'cache/'
    
    filepath = dir + '_config_' + hda.parm('preset').evalAsString() + '.json'
    
    data = {}
    ramp = {}
    ramps = {}
    mp = {}

    ### Gather Standard and Ramp Parameters
    #######################################

    for parm in params: # Loop Through All Parameters in Folder
        if not parm.isHidden():
            if parm.isMultiParmParent(): # First Loop Through Ramp Parameters
            
                rampname = parm.name()
                ramp = parm.evalAsRamp()
                interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
    
                keylist = []
                for i,key in enumerate(ramp.keys()):
                    rampdata = { 'posindex': i,
                        'pos' : key,   
                        'value': ramp.values()[i],
                        'interp': interplookup.index(ramp.basis()[i])}    
                    keylist.append(rampdata)

                ramp = { rampname: keylist }
                ramps.update(ramp) # Write Ramp Parameters with the Ramp Name as Wrapper to <ramps>
                
                # <continue> ends the for loop’s current iteration https://www.coursera.org/tutorials/python-while-loop
                # and skips ramp parameters when creating the data dictionary in the next step
                # ramps consist of multiple elements like multiparms and result in errors when not excluded from the standard evaluation
                continue

            data[parm.name()] = parm.eval() # Write Remaining Standard Parameters to <data>

    ### Initialize List with Names of Parameter Groups
    ##################################################

    allparm = hda.parms() # All Parameters
    ramplist = list(ramps.keys()) # Ramps Outside of Multiparms
    mplist = [] # Multiparm Folders
    mpramplist = [] # Ramps Inside Multiparms

    ### Create Lists with Names of Multiparms + Ramps Outside / Inside of Multiparms
    ################################################################################

    for mpi in allparm: # For Every Parameter
        if not mpi.isHidden():
            if mpi.isMultiParmParent(): # If the Parameter is a Multiparm Parent
                allparmname = mpi.name()
                # Check if the Parameter has a Parent - Returns the Parent for Ramps Inside Multiparms but None for Multiparms
                pmp = mpi.parentMultiParm()
                if not pmp == None:
                    mpramplist.append(allparmname)
                if not allparmname in ramplist and pmp == None: # If the Parameter Name is not in the Ramplist and has no Parent
                    mplist.append(allparmname) # Update mplist and Append allparmname

    ### Gather Multiparm Parameters
    ###############################

    for mpi in mplist: # For Every Multiparm Folder
        mparam = hda.parm(mpi)
        
        mp_inst = {
            mpi: []
        }
        
        num = hda.parm(mpi).evalAsInt() # Evaluate the Number of the Multiparm Instances
        instances = hda.parm(mpi).multiParmInstances() # Get the Parameters Within the Multiparm Instances

        for i in range(num): # For Every Multiparm (Folder)

            block_data = {}

            for instance in instances: # For Every Parameter Within the Multiparm Instances
                
                # Index Calculation #
                #...................#
                instname = instance.name()
                revinstname = instname[::-1] # Reverse Instance Name
                if revinstname[0].isdigit(): # If First Character is Digit
                    cleaninstname = revinstname.lstrip('0123456789') # Remove Digits From the Beginning (Reversed Ending)
                    strip = len(cleaninstname) # Get Length of Pure Instance Name (Without Digits)
                    index = instname[strip:] # Slice Index Number out of Instance Name
                else: # In Case of a Character Suffix (Houdini x y z Vector)
                    cleaninstname = revinstname[1:].lstrip('0123456789')
                    strip = len(cleaninstname)
                    index = instname[strip:-1] # Slice Index Number out of Instance Name and Remove Vector Suffix (x y z)
                #...................#

                # For all Parameters in Multiparm except Ramps #
                #..............................................#
                if not instname in mpramplist and int(index) == i+1: # If Instance Name is not in List of Ramps (Inside Multiparms)
        
                    name = instance.name()
                    value = instance.eval()
                    block_data[name] = value
                #..............................................#

                # For Ramps in Multiparms #
                #.........................#
                if instname in mpramplist and int(index) == i+1:
                    mprampname = instance.name()
                    mpramp = instance.evalAsRamp()
                    interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
        
                    mpkeylist = []
                    for ir,key in enumerate(mpramp.keys()):
                        rampdata = { 'posindex': ir,
                            'pos' : key,   
                            'value': mpramp.values()[ir],
                            'interp': interplookup.index(mpramp.basis()[ir])}    
                        mpkeylist.append(rampdata)

                    mpramps = { mprampname: mpkeylist }
                    block_data.update(mpramps) # Add Ramps to Block Data 
                #.........................#

            mp_inst[mpi].append(block_data)
            mp.update(mp_inst)

    datas = { stdgroup: data } # Wrapper for Standard Parameters
    ramps = { rampgroup: ramps } # Wrapper for Ramp Parameters
    mps = { mpgroup: mp } # Wrapper for Multiparms

    datas.update(ramps) # Append Ramp Values to the Final Dictionary
    datas.update(mps) # Append the Multiparm Values to the Final Dictionary
            
    if not os.path.exists(dir):
        os.makedirs(dir)
            
    with open(filepath, 'w') as outfile:
        json.dump(datas, outfile, indent=4)

    print('Parameters exported to:', filepath)
        
#########################
### Import Parameters ###
#########################

def read_params(kwargs):
    
    hda = kwargs['node']
    params = hda.parmsInFolder(['Parameters'])
    
    from sys import platform
    
    dir = hda.parm('path').evalAsString() + '/cache/'
    if platform == 'darwin':
        dir = hda.parm('path').evalAsString() + 'cache/'
        
    filepath = dir + '_config_' + hda.parm('preset').evalAsString() + '.json'
        
    if len(filepath) > 0 and os.path.isfile(filepath):
        
        with open(filepath, 'r') as outfile:
            parameters = json.load(outfile)[stdgroup] # Read JSON for Standard Parameters
        with open(filepath, 'r') as outfile:
            rparameters = json.load(outfile)[rampgroup] # Read JSON for Ramp Parameters
        with open(filepath, 'r') as outfile:
            mparameters = json.load(outfile)[mpgroup] # Read JSON for Multiparms

        
        # Set Standard Parameters #
        #.........................#
        for parm_name, value in parameters.items():
            parm = hda.parm(parm_name)
            if parm is not None:
                parm.set(value)
        #.........................#

        # Set Ramp Parameters #
        #.....................#
        for parm_name, value in rparameters.items(): # Runs Over Every Ramp Parameter Name
            parm = hda.parm(parm_name)

            if parm is not None:
                keys = []
                values = []
                bases = []
                interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
                for i in rparameters[parm_name]: # Query Current Ramp Name With <[parm_name]>
                    keys.append(i['pos'])
                    values.append(i['value'])
                    bases.append(interplookup[i['interp']])

                ramp = hou.Ramp(bases, keys, values) # Initialize New Ramp
                parm.set(ramp) # Set Ramp Parameters
        #.....................#
        
        # Set Multiparms #
        #................#
        for parm_name, value in mparameters.items(): # Runs Over Every Multiparm (Folder) Name
            parm = hda.parm(parm_name)
            parm.set(0)

            if len(mparameters) > 0:
                blocks = mparameters[parm_name] # Extract all Parameters from Within Each Multiparm (Folder) as Blocks

                for ib in range(len(blocks)): # For Every Block Within Every Multiparm (Folder)
                    parm.insertMultiParmInstance(ib)
                
                    for name, value in blocks[ib].items(): # Test Dictionary Depth https://www.geeksforgeeks.org/python-find-depth-of-a-dictionary/

                        def dict_depth(dic, level = 1):
      
                            str_dic = str(dic)
                            counter = 0
                            for i in str_dic:
                                if i == "{": # Curly Braces Indicate Nesting Depth
                                    counter += 1
                            return(counter)
                        dic = value

                        # Set Standard Multiparm Parameters #
                        #...................................#
                        if dict_depth(dic) == 0: # No Curly Braces - no Ramp
                            hda.parm(name).set(value)
                        #...................................#

                        # Set Ramps in Multiparms #
                        #.........................#
                        else:  # Min. One Curly Brace - Ramp

                            if parm is not None:
                                keys = []
                                values = []
                                bases = []
                                interplookup = [hou.rampBasis.Constant, hou.rampBasis.Linear, hou.rampBasis.CatmullRom, hou.rampBasis.MonotoneCubic, hou.rampBasis.Bezier, hou.rampBasis.BSpline, hou.rampBasis.Hermite]
                                for im in value: # Inner Loop - We Query the Current Ramp Name With <[parm_name]>
                                    keys.append(im['pos'])
                                    values.append(im['value'])
                                    bases.append(interplookup[im['interp']])

                                ramp = hou.Ramp(bases, keys, values) # Initialize New Ramp
                                hda.parm(name).set(ramp)
                        #.........................#
        #................#
                
        print('Parameters imported from:', filepath)
        
    else:
        hou.ui.displayMessage('Couldnt find the file!!')
#<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

Attachments:
FF_JSON_Export_1.0.hipnc (180.1 KB)

  • Quick Links