mirror of https://github.com/H-uru/korman.git
Browse Source
This effectively bumps the minimum requirement to Blender 2.79. Furthermore, any blendfiles saved with ID Datablock properties will crash earlier versions of Blender. You have been warned... After approximately 24 hours of writing, rewriting, and cursing Blender, this appears to be the most flexible way of magically upgrading our old string properties to ID Datablock properties. The general hacky-ness is due to limitations in Blender's API. Here's how it works... In your property group (node, modifier, etc) you will need to implement the classmethod `_idprop_mapping`. This will map new ID Datablock property attribute names to old string property attribute names. Further, you will need to implement the `_idprop_sources` method. This will map string property attribute names to a collection to fetch the ID from. If you have specific filtering rules to follow, they can be implemented here :). To ensure code sanity, any attempts to access string properties that have been marked as converted will now fail with an AttributeError. Happy haxxoring!pull/56/head
Adam Johnson
8 years ago
2 changed files with 119 additions and 1 deletions
@ -0,0 +1,118 @@ |
|||||||
|
# This file is part of Korman. |
||||||
|
# |
||||||
|
# Korman is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# Korman is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with Korman. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
import bpy |
||||||
|
from bpy.props import * |
||||||
|
|
||||||
|
class IDPropMixin: |
||||||
|
""" |
||||||
|
So, here's the rub. |
||||||
|
|
||||||
|
In Blender 2.79, we finally get the ability to use native Blender ID Datablock properties in Python. |
||||||
|
This is great! It will allow us to specify other objects (Blender Objects, Materials, Textures) in |
||||||
|
our plugin as pointer properties. Further, we can even specify a poll method to create a 'search list' |
||||||
|
of valid options. |
||||||
|
|
||||||
|
Naturally, there are some cons. The con here is that we've been storing object NAMES in string properties |
||||||
|
for several releases now. Therefore, the purpose of this class is simple... It is a mixin to be |
||||||
|
used for silently upgrading these object name properties to ID Properties. You will need to override |
||||||
|
the _idprop_mapping and _idprop_sources methods in your class. The mixin will handle upgrading |
||||||
|
the properties when a derived class is touched. |
||||||
|
|
||||||
|
Unfortunately, it is not possible to easily batch convert everything on load or save, due to issues |
||||||
|
in the way Blender's Python API functions. Long story short: PropertyGroups do not execute __new__ |
||||||
|
or __init__. Furthermore, Blender's UI does not appreciate having ID Datablocks return from |
||||||
|
__getattribute__. To make matters worse, all properties are locked in a read-only state during |
||||||
|
the UI draw stage. |
||||||
|
""" |
||||||
|
|
||||||
|
def __getattribute__(self, attr): |
||||||
|
_getattribute = super().__getattribute__ |
||||||
|
|
||||||
|
# Let's make sure no one is trying to access an old version... |
||||||
|
if attr in _getattribute("_idprop_mapping")().values(): |
||||||
|
raise AttributeError("'{}' has been deprecated... Please use the ID Property".format(attr)) |
||||||
|
|
||||||
|
# I have some bad news for you... Unfortunately, this might have been called |
||||||
|
# during Blender's draw() context. Blender locks all properties during the draw loop. |
||||||
|
# HOWEVER!!! There is a solution. Upon inspection of the Blender source code, however, it |
||||||
|
# appears this restriction is temporarily suppressed during property getters... So let's get |
||||||
|
# a property that executes a getter :D |
||||||
|
# ... |
||||||
|
# ... |
||||||
|
# But why not simply proxy requests here, you ask? Ah, young grasshopper... This is the |
||||||
|
# fifth time I have (re-)written this code. Trust me when I say, 'tis a boondoggle. |
||||||
|
assert _getattribute("idprops_upgraded") |
||||||
|
|
||||||
|
# Must be something regular. Just super it. |
||||||
|
return super().__getattribute__(attr) |
||||||
|
|
||||||
|
def __setattr__(self, attr, value): |
||||||
|
idprops = super().__getattribute__("_idprop_mapping")() |
||||||
|
|
||||||
|
# Disallow any attempts to set the old string property |
||||||
|
if attr in idprops.values(): |
||||||
|
raise AttributeError("'{}' has been deprecated... Please use the ID Property".format(attr)) |
||||||
|
|
||||||
|
# Inappropriate touching? |
||||||
|
super().__getattribute__("_try_upgrade_idprops")() |
||||||
|
|
||||||
|
# Now, pass along our update |
||||||
|
super().__setattr__(attr, value) |
||||||
|
|
||||||
|
@classmethod |
||||||
|
def register(cls): |
||||||
|
if hasattr(super(), "register"): |
||||||
|
super().register() |
||||||
|
|
||||||
|
cls.idprops_upgraded = BoolProperty(name="INTERNAL: ID Property Upgrader HACK", |
||||||
|
description="HAAAX *throws CRT monitor*", |
||||||
|
get=cls._try_upgrade_idprops, |
||||||
|
options={"HIDDEN"}) |
||||||
|
cls.idprops_upgraded_value = BoolProperty(name="INTERNAL: ID Property Upgrade Status", |
||||||
|
description="Have old StringProperties been upgraded to ID Datablock Properties?", |
||||||
|
default=False, |
||||||
|
options={"HIDDEN"}) |
||||||
|
for str_prop in cls._idprop_mapping().values(): |
||||||
|
setattr(cls, str_prop, StringProperty(description="deprecated")) |
||||||
|
|
||||||
|
def _try_upgrade_idprops(self): |
||||||
|
_getattribute = super().__getattribute__ |
||||||
|
|
||||||
|
if not _getattribute("idprops_upgraded_value"): |
||||||
|
idprop_map = _getattribute("_idprop_mapping")() |
||||||
|
strprop_src = _getattribute("_idprop_sources")() |
||||||
|
|
||||||
|
for idprop_name, strprop_name in idprop_map.items(): |
||||||
|
if not super().is_property_set(strprop_name): |
||||||
|
continue |
||||||
|
strprop_value = _getattribute(strprop_name) |
||||||
|
idprop_value = strprop_src[strprop_name].get(strprop_value, None) |
||||||
|
super().__setattr__(idprop_name, idprop_value) |
||||||
|
super().property_unset(strprop_name) |
||||||
|
super().__setattr__("idprops_upgraded_value", True) |
||||||
|
|
||||||
|
# you should feel like this now... https://youtu.be/1JBSs6MQJeI?t=33s |
||||||
|
return True |
||||||
|
|
||||||
|
|
||||||
|
class IDPropObjectMixin(IDPropMixin): |
||||||
|
"""Like IDPropMixin, but with the assumption that all IDs can be found in bpy.data.objects""" |
||||||
|
|
||||||
|
def _idprop_sources(self): |
||||||
|
# NOTE: bad problems result when using super() here, so we'll manually reference object |
||||||
|
cls = object.__getattribute__(self, "__class__") |
||||||
|
idprops = cls._idprop_mapping() |
||||||
|
return { i: bpy.data.objects for i in idprops.values() } |
Loading…
Reference in new issue