Is there a better way to make addon working on both blender 2.80 and 2.79?
$begingroup$
I know this works:
import bpy
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
if bpy.app.version >= (2, 80, 0):
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
else:
type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name= bpy.props.StringProperty(update=unedit)
bpy.utils.register_class(Test)
but it is coping and paste solution (bed for many properties). Is there a better way?
python add-on compatibility
$endgroup$
add a comment |
$begingroup$
I know this works:
import bpy
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
if bpy.app.version >= (2, 80, 0):
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
else:
type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name= bpy.props.StringProperty(update=unedit)
bpy.utils.register_class(Test)
but it is coping and paste solution (bed for many properties). Is there a better way?
python add-on compatibility
$endgroup$
add a comment |
$begingroup$
I know this works:
import bpy
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
if bpy.app.version >= (2, 80, 0):
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
else:
type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name= bpy.props.StringProperty(update=unedit)
bpy.utils.register_class(Test)
but it is coping and paste solution (bed for many properties). Is there a better way?
python add-on compatibility
$endgroup$
I know this works:
import bpy
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
if bpy.app.version >= (2, 80, 0):
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
else:
type= bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name= bpy.props.StringProperty(update=unedit)
bpy.utils.register_class(Test)
but it is coping and paste solution (bed for many properties). Is there a better way?
python add-on compatibility
python add-on compatibility
asked 16 hours ago
Piotr KowalczykPiotr Kowalczyk
765
765
add a comment |
add a comment |
4 Answers
4
active
oldest
votes
$begingroup$
Two separate addons
The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.
I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)
Distribute as an addon for that version. If using github a branch is often used.
If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py
check once for version and import modules from that version folder.
See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130
$endgroup$
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
add a comment |
$begingroup$
I am answering my own question, maybe someone finds it useful.
import bpy
class Test_2_80(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
class Test(Test_2_80):
if bpy.app.version < (2, 80, 0):
for val, fun in Test_2_80.__annotations__.items():
exec('{} = fun'.format(val))
val, fun = None, None
else:
pass
bpy.utils.register_class(Test)
bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
bpy.context.scene.test.name = 'g'
Any ideas for better solutions?
$endgroup$
add a comment |
$begingroup$
The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.
Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.
In python this is not so easy. You can use __debug__
which is pre-processed by the compiler and evaluated based on -O
command line argument and any if False:
part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.
The solution I found is to use pypreprocessor. It lets you do:
from pypreprocessor import pypreprocessor
pypreprocessor.parse()
#define 2_80
#ifdef 2_80
print('2_80 code')
#else
print('2_79 code')
#endif
Normally when you call .parse()
it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.
The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:
pypreprocessor.defines.append('define')
pypreprocessor.run = False
pypreprocessor.save = True
pypreprocessor.output = 'addon_2_80.py'
pypreprocessor.removeMeta = True
This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.
$endgroup$
add a comment |
$begingroup$
Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).
Warning: I also have not tested either.
Another option is to create a decorator:
import bpy
def copy_annotations_to_dict_if_old_blender(x):
if bpy.app.version < (2, 80):
x.__dict__.update(x.__annotations__)
return x
@copy_annotations_to_dict_if_old_blender
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
Previous code that used a metaclass instead:
import bpy
if bpy.app.version >= (2, 80):
MyPropertyGroupMeta = bpy.RNAMetaPropGroup
else:
class MyPropertyGroup(bpy.types.PropertyGroup):
def __new__(cls, name, bases, namespace, **kwargs):
result = super().__new__(cls, name, bases, namespace, **kwargs)
result.__dict__.update(result.__annotations__)
return result
class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
__slots__ = ()
class Test(MyPropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
(the decorator code is more readable and more likely to work)
New contributor
$endgroup$
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
});
});
}, "mathjax-editing");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "502"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fblender.stackexchange.com%2fquestions%2f133457%2fis-there-a-better-way-to-make-addon-working-on-both-blender-2-80-and-2-79%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Two separate addons
The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.
I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)
Distribute as an addon for that version. If using github a branch is often used.
If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py
check once for version and import modules from that version folder.
See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130
$endgroup$
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
add a comment |
$begingroup$
Two separate addons
The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.
I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)
Distribute as an addon for that version. If using github a branch is often used.
If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py
check once for version and import modules from that version folder.
See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130
$endgroup$
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
add a comment |
$begingroup$
Two separate addons
The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.
I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)
Distribute as an addon for that version. If using github a branch is often used.
If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py
check once for version and import modules from that version folder.
See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130
$endgroup$
Two separate addons
The code in question and your answer has versioning for one part of the changes from 2.79 to 2.80, ie annotations in property groups.
I would suggest writing two addons, one that works in 2.79 and one that works in 2.80. (As opposed to having versioning code ad hoc in each py file.)
Distribute as an addon for that version. If using github a branch is often used.
If wish to distribute as a single addon that works in both, suggest putting each in a subfolder of the root folder of the addon, then in the addons __init__.py
check once for version and import modules from that version folder.
See this post on fake modules for some cool trickery https://stackoverflow.com/a/27476659/5317130
answered 15 hours ago
batFINGERbatFINGER
25.1k42674
25.1k42674
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
add a comment |
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
$begingroup$
I think this approach is best since many other changes exist in 2.8 that have to be fixed for a 2.79 script to work there.
$endgroup$
– Craig D Jones
15 hours ago
add a comment |
$begingroup$
I am answering my own question, maybe someone finds it useful.
import bpy
class Test_2_80(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
class Test(Test_2_80):
if bpy.app.version < (2, 80, 0):
for val, fun in Test_2_80.__annotations__.items():
exec('{} = fun'.format(val))
val, fun = None, None
else:
pass
bpy.utils.register_class(Test)
bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
bpy.context.scene.test.name = 'g'
Any ideas for better solutions?
$endgroup$
add a comment |
$begingroup$
I am answering my own question, maybe someone finds it useful.
import bpy
class Test_2_80(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
class Test(Test_2_80):
if bpy.app.version < (2, 80, 0):
for val, fun in Test_2_80.__annotations__.items():
exec('{} = fun'.format(val))
val, fun = None, None
else:
pass
bpy.utils.register_class(Test)
bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
bpy.context.scene.test.name = 'g'
Any ideas for better solutions?
$endgroup$
add a comment |
$begingroup$
I am answering my own question, maybe someone finds it useful.
import bpy
class Test_2_80(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
class Test(Test_2_80):
if bpy.app.version < (2, 80, 0):
for val, fun in Test_2_80.__annotations__.items():
exec('{} = fun'.format(val))
val, fun = None, None
else:
pass
bpy.utils.register_class(Test)
bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
bpy.context.scene.test.name = 'g'
Any ideas for better solutions?
$endgroup$
I am answering my own question, maybe someone finds it useful.
import bpy
class Test_2_80(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
class Test(Test_2_80):
if bpy.app.version < (2, 80, 0):
for val, fun in Test_2_80.__annotations__.items():
exec('{} = fun'.format(val))
val, fun = None, None
else:
pass
bpy.utils.register_class(Test)
bpy.types.Scene.test = bpy.props.PointerProperty(type=Test)
bpy.context.scene.test.name = 'g'
Any ideas for better solutions?
answered 16 hours ago
Piotr KowalczykPiotr Kowalczyk
765
765
add a comment |
add a comment |
$begingroup$
The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.
Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.
In python this is not so easy. You can use __debug__
which is pre-processed by the compiler and evaluated based on -O
command line argument and any if False:
part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.
The solution I found is to use pypreprocessor. It lets you do:
from pypreprocessor import pypreprocessor
pypreprocessor.parse()
#define 2_80
#ifdef 2_80
print('2_80 code')
#else
print('2_79 code')
#endif
Normally when you call .parse()
it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.
The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:
pypreprocessor.defines.append('define')
pypreprocessor.run = False
pypreprocessor.save = True
pypreprocessor.output = 'addon_2_80.py'
pypreprocessor.removeMeta = True
This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.
$endgroup$
add a comment |
$begingroup$
The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.
Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.
In python this is not so easy. You can use __debug__
which is pre-processed by the compiler and evaluated based on -O
command line argument and any if False:
part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.
The solution I found is to use pypreprocessor. It lets you do:
from pypreprocessor import pypreprocessor
pypreprocessor.parse()
#define 2_80
#ifdef 2_80
print('2_80 code')
#else
print('2_79 code')
#endif
Normally when you call .parse()
it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.
The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:
pypreprocessor.defines.append('define')
pypreprocessor.run = False
pypreprocessor.save = True
pypreprocessor.output = 'addon_2_80.py'
pypreprocessor.removeMeta = True
This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.
$endgroup$
add a comment |
$begingroup$
The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.
Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.
In python this is not so easy. You can use __debug__
which is pre-processed by the compiler and evaluated based on -O
command line argument and any if False:
part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.
The solution I found is to use pypreprocessor. It lets you do:
from pypreprocessor import pypreprocessor
pypreprocessor.parse()
#define 2_80
#ifdef 2_80
print('2_80 code')
#else
print('2_79 code')
#endif
Normally when you call .parse()
it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.
The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:
pypreprocessor.defines.append('define')
pypreprocessor.run = False
pypreprocessor.save = True
pypreprocessor.output = 'addon_2_80.py'
pypreprocessor.removeMeta = True
This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.
$endgroup$
The problem I have with making a code that works in both versions is it gets more complicated and more evaluation needs to happen at runtime. There is extra code and 'baggage' than in either version would be.
Normally in c/c++ you would implement pre-processor directives into the code to organize it and generate an executable of only what you need.
In python this is not so easy. You can use __debug__
which is pre-processed by the compiler and evaluated based on -O
command line argument and any if False:
part of code will be skipped by the optimizer, but you have to run the code with arguments which is not convenient for Blender use.
The solution I found is to use pypreprocessor. It lets you do:
from pypreprocessor import pypreprocessor
pypreprocessor.parse()
#define 2_80
#ifdef 2_80
print('2_80 code')
#else
print('2_79 code')
#endif
Normally when you call .parse()
it generates a temporary copy with pypreprocessor commented (not to run recursively) and also any unused code commented out and this post-processed code is executed on-the-fly.
The best thing about it is instead of executing the post-processed code you can output to a file with a user-defined filename and even remove all the preprocessor directives and preprocessor specific code:
pypreprocessor.defines.append('define')
pypreprocessor.run = False
pypreprocessor.save = True
pypreprocessor.output = 'addon_2_80.py'
pypreprocessor.removeMeta = True
This way you get clean files for 2.80 or 2.79 versions, that no one knows were generated from the same source.
answered 14 hours ago
Jaroslav Jerryno NovotnyJaroslav Jerryno Novotny
39.3k176155
39.3k176155
add a comment |
add a comment |
$begingroup$
Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).
Warning: I also have not tested either.
Another option is to create a decorator:
import bpy
def copy_annotations_to_dict_if_old_blender(x):
if bpy.app.version < (2, 80):
x.__dict__.update(x.__annotations__)
return x
@copy_annotations_to_dict_if_old_blender
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
Previous code that used a metaclass instead:
import bpy
if bpy.app.version >= (2, 80):
MyPropertyGroupMeta = bpy.RNAMetaPropGroup
else:
class MyPropertyGroup(bpy.types.PropertyGroup):
def __new__(cls, name, bases, namespace, **kwargs):
result = super().__new__(cls, name, bases, namespace, **kwargs)
result.__dict__.update(result.__annotations__)
return result
class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
__slots__ = ()
class Test(MyPropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
(the decorator code is more readable and more likely to work)
New contributor
$endgroup$
add a comment |
$begingroup$
Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).
Warning: I also have not tested either.
Another option is to create a decorator:
import bpy
def copy_annotations_to_dict_if_old_blender(x):
if bpy.app.version < (2, 80):
x.__dict__.update(x.__annotations__)
return x
@copy_annotations_to_dict_if_old_blender
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
Previous code that used a metaclass instead:
import bpy
if bpy.app.version >= (2, 80):
MyPropertyGroupMeta = bpy.RNAMetaPropGroup
else:
class MyPropertyGroup(bpy.types.PropertyGroup):
def __new__(cls, name, bases, namespace, **kwargs):
result = super().__new__(cls, name, bases, namespace, **kwargs)
result.__dict__.update(result.__annotations__)
return result
class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
__slots__ = ()
class Test(MyPropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
(the decorator code is more readable and more likely to work)
New contributor
$endgroup$
add a comment |
$begingroup$
Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).
Warning: I also have not tested either.
Another option is to create a decorator:
import bpy
def copy_annotations_to_dict_if_old_blender(x):
if bpy.app.version < (2, 80):
x.__dict__.update(x.__annotations__)
return x
@copy_annotations_to_dict_if_old_blender
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
Previous code that used a metaclass instead:
import bpy
if bpy.app.version >= (2, 80):
MyPropertyGroupMeta = bpy.RNAMetaPropGroup
else:
class MyPropertyGroup(bpy.types.PropertyGroup):
def __new__(cls, name, bases, namespace, **kwargs):
result = super().__new__(cls, name, bases, namespace, **kwargs)
result.__dict__.update(result.__annotations__)
return result
class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
__slots__ = ()
class Test(MyPropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
(the decorator code is more readable and more likely to work)
New contributor
$endgroup$
Note: I have a lot of experience with Python, but no experience with Blender (I found the source code to look through).
Warning: I also have not tested either.
Another option is to create a decorator:
import bpy
def copy_annotations_to_dict_if_old_blender(x):
if bpy.app.version < (2, 80):
x.__dict__.update(x.__annotations__)
return x
@copy_annotations_to_dict_if_old_blender
class Test(bpy.types.PropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
Previous code that used a metaclass instead:
import bpy
if bpy.app.version >= (2, 80):
MyPropertyGroupMeta = bpy.RNAMetaPropGroup
else:
class MyPropertyGroup(bpy.types.PropertyGroup):
def __new__(cls, name, bases, namespace, **kwargs):
result = super().__new__(cls, name, bases, namespace, **kwargs)
result.__dict__.update(result.__annotations__)
return result
class MyPropertyGroup(bpy.types.StructRNA, metaclass=MyPropertyGroupMeta):
__slots__ = ()
class Test(MyPropertyGroup):
def unedit(self, context):
print(self.type)
type: bpy.props.StringProperty(default='xxxxxxxxxxxxxxxxxx')
name: bpy.props.StringProperty(update=unedit)
(the decorator code is more readable and more likely to work)
New contributor
New contributor
answered 5 hours ago
Solomon UckoSolomon Ucko
1012
1012
New contributor
New contributor
add a comment |
add a comment |
Thanks for contributing an answer to Blender Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fblender.stackexchange.com%2fquestions%2f133457%2fis-there-a-better-way-to-make-addon-working-on-both-blender-2-80-and-2-79%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown