6
$\begingroup$

Operators have a check() function that Blender calls when it wants to check if it should redraw. Exporters usually use this to attach a file extension to the file name (eg).

I know check() already runs after the user has edited the filename field, or after they click a file in the filepicker. I would like it to run after the user changes an option too. For example, the glTF exporter does this:

Filename changes with option

If the prop is drawn by the operator.draw() function this happens automatically. However it does not happen automatically if the option is drawn by a separate panel. How can I get it to update in that case?

Here is a complete example you can test with: https://pastebin.com/2k6mBNGz

(PS. I already know how the glTF exporter does it. I'm interested in a "proper" solution.)

$\endgroup$
3
  • $\begingroup$ Could you please elaborate on how the glTF exporter does it? $\endgroup$
    – batFINGER
    Commented Jan 8, 2021 at 11:31
  • $\begingroup$ @batFINGER The EnumProperty is given an update function that calls the operator bpy.ops.file.filenum (that's the plus/minus buttons in the file field that add numbers to the end of the filename) in a way designed to have no net change on the filename. $\endgroup$
    – scurest
    Commented Jan 8, 2021 at 17:54
  • $\begingroup$ Oh Ok. Have answered similar in that using an update method, however simply adjusting the spacedata field for file name instead of using the operator. $\endgroup$
    – batFINGER
    Commented Jan 9, 2021 at 15:38

2 Answers 2

7
+250
$\begingroup$

Set the filebrowser filename

The code for the ExportHelper mixin class is shown in this question Right way to stop export operator from saving with empty filename?

As demonstrated in question it appears only the FILE_PT_operator is live wired to update the parameters using check.

def update_ext(self, context):
    # if we wish to set filename_ext
    self.filename_ext = f".{self.extension.lower()}"
    #the filebrowser parameters
    params  = context.space_data.params
    # printed to console
    for k in dir(params):
        print(k, getattr(params, k))
    # set the file name  
    params.filename = f"{Path(self.filepath).stem}.{self.extension.lower()}"   

And use this as an update on the operator enum

extension: bpy.props.EnumProperty(
    name='Extension',
    items=(('FOO', '.foo', '.foo'), ('BAR', '.bar', '.bar')),
    default='FOO',
    update = update_ext,
)

If also using filename_ext make it a property and give it a default to match the enum, however can pretty much do without. The check_existing if set will deal with alerting re pre-existing. (Assume it does haven't checked)

So can pretty much replace the check method to redraw the file browser properties.

$\endgroup$
2
  • $\begingroup$ Thanks a lot, this is an improvement over the filenum operator :) One addition: context.space_data needs some checks to make sure it's the expected file browser, or else the operator won't work when eg. called from the Python console. $\endgroup$
    – scurest
    Commented Jan 9, 2021 at 20:27
  • $\begingroup$ When I try to do this in my own addon, the console barks at me with this message: "AttributeError: 'EXPORT_OT_gml' object has no attribute 'filename_ext'" - why might this happen? Am I using my update_ext() in the wrong context? $\endgroup$
    – Marty
    Commented Jun 25, 2021 at 7:06
1
$\begingroup$

you can use getter/setter functions for these kind of updates or can check them in modal functions by checking left clicks.

an example of getter/setter functions is described in API like below:

import bpy


# Simple property reading/writing from ID properties.
# This is what the RNA would do internally.
def get_float(self):
    # do some stuff here before get
    return self.get("myfloat",0.0)

# self in this method is the object which you attach the property to
# in this case self is any scene in bpy.data for example bpy.data.scenes[0]
def set_float(self, value):
    # do some stuff here before/after set because no return is required
    self["myfloat"] = value


bpy.types.Scene.myfloat = bpy.props.FloatProperty(get=get_float, set=set_float)

now whenever you get/set a scene.myfloat data above methods work.

$\endgroup$
3
  • 2
    $\begingroup$ Recommend you test this. Returning the "got" value from the getter will recurse. Use the ID prop to emulate what the rna does. blender.stackexchange.com/a/134310/15543 $\endgroup$
    – batFINGER
    Commented Jan 8, 2021 at 11:35
  • $\begingroup$ Thanks @batFinger. You are true with this. I've updated my answer. $\endgroup$ Commented Jan 8, 2021 at 13:32
  • $\begingroup$ Sorry, I don't follow. Can you please phrase the answer as a diff on the pastebin in the question that will make it work? $\endgroup$
    – scurest
    Commented Jan 8, 2021 at 18:04

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .