Page MenuHome

UI/BPY: Allow Python operator polls to set the disabled hint
AbandonedPublic

Authored by Julian Eisel (Severin) on Dec 3 2020, 6:21 PM.

Details

Summary

We generally want to use the "disabled hint" more often (see T76261), which
is a red text in tooltips that explains why a button is disabled. So far this can't
be done from Python at all. This is a first step to get it supported.

This is a low-effort attempt, but I think it's a reasonable way to go about it.
To set the hint, in a poll method, Python operators can set it like this:

context.operator_poll_message = "Can't do this, because of reasons"
return False

The string is stored as copy in an allocated buffer. Not sure if this will work
with translations though, ie obtaining the string with IFACE_().

Diff Detail

Repository
rB Blender
Branch
temp-bpy-operator-poll-msg (branched from master)
Build Status
Buildable 11549
Build 11549: arc lint + arc unit

Event Timeline

Julian Eisel (Severin) requested review of this revision.Dec 3 2020, 6:21 PM
Julian Eisel (Severin) created this revision.
Julian Eisel (Severin) edited the summary of this revision. (Show Details)Dec 3 2020, 6:25 PM
This revision is now accepted and ready to land.Dec 4 2020, 10:27 AM

LGTM too, don't think this should be an issue with i18n code.

Thinks @Campbell Barton (campbellbarton) will want to check that too though?

This revision now requires review to proceed.Dec 9 2020, 11:04 AM

I don't think there is other code that requires Python to actually set things in the context, so personally I wouldn't intuitively end up at this solution if I were looking for a way to do this.

How hard would it be to allow a poll function to return not only an "ok" boolean, but also a tuple ("ok", "reason")? As an alternative we could simply return a string and consider that short-hand for "false, and this is why", but to me that feels a bit dirty. I'd rather have the choice of returning one of True, False, or (False, "reason").

My concern with this patch is poll functions which are meant to be fast/simple will end up with string creation logic when it's not needed.

Scripts, especially developers that aren't considering performance could construct detailed explanations why poll fails, which will slow down poll functions.

I'd rather assign a callback than a string to allow delayed evaluation, e.g:

context.poll_disable_message(lambda: "some string")

Or...

context.poll_disable_message(detailed_function, optional_args_to_funciton)

@Sybren A. Stüvel (sybren) good points, I like your idea.
AFAIK the main reason the operator poll() doesn't take context as const is to be able to set the disabled hint. Eventually I'd like to see this addressed, and not having the Python code rely on this is a good idea I think, might save us some trouble later on.

RNA can return tuples, we just need to check how it can support either returning a tuple (boolean, string) or a single boolean, as we discussed in the chat.

Having extra optional return values has the down side that you couldn't reliably use a poll function from another operator, as changing the return value would cause if OtherOperator.poll(C): ... to always evaluate to true.

Even knowing what to expect is awkward to test:

poll_test = OtherOperator.poll(C)
if poll_test if isinstance(poll_test, bool) else poll_test[0]: ...

It also doesn't fit so well with delayed evaluation (although it could return a structure that would be evaluated later).

Submitted alternate patch that supports defering the evaluation and only allocates the string when it's needed: D11001

Having extra optional return values has the down side that you couldn't reliably use a poll function from another operator, as changing the return value would cause if OtherOperator.poll(C): ... to always evaluate to true.

Good point, that's the death of my idea right there.

We could return a new PollResult object, which takes a boolean and a string, and with a __bool__ function that returns the given boolean. That'll make the truthyness check transparent.

It also doesn't fit so well with delayed evaluation (although it could return a structure that would be evaluated later).

A PollResult could handle this gracefully I think.

Committed rBebe04bd3cafaa1f88bd51eee5b3e7bef38ae69bc which implements this functionality, closing.