Is there a better way to make addon working on both blender 2.80 and 2.79?How to create toggle buttons in...
Can you 'upgrade' leather armor to studded leather armor without purchasing the new armor directly?
Second-rate spelling
Make me a metasequence
Where was Karl Mordo in Infinity War?
How can atoms be electrically neutral when there is a difference in the positions of the charges?
When was drinking water recognized as crucial in marathon running?
It took me a lot of time to make this, pls like. (YouTube Comments #1)
Are small insurances worth it
Which aircraft had such a luxurious-looking navigator's station?
What's the difference between a cart and a wagon?
How to deny access to SQL Server to certain login over SSMS, but allow over .Net SqlClient Data Provider
How to count occurrences of Friday 13th
Borrowing Characters
As a new poet, where can I find help from a professional to judge my work?
GeometricMean definition
Where is this triangular-shaped space station from?
What do the pedals on grand pianos do?
Is it 40% or 0.4%?
Is there any relevance to Thor getting his hair cut other than comedic value?
I can't die. Who am I?
How to approximate rolls for potions of healing using only d6's?
Why is working on the same position for more than 15 years not a red flag?
Non-Italian European mafias in USA?
What to do when being responsible for data protection in your lab, yet advice is ignored?
Is there a better way to make addon working on both blender 2.80 and 2.79?
How to create toggle buttons in blender GUI from dynamic list of items?Python modifier error handlingWhats the best way to make an operator tooltip depend on a Blender version?Conflicts between addons contexts?Modal operators not working when used through pop-up menuinconsistent AttributeError when modifying frame_currentPython: Using pycharm with the blender API gives errorsAdd a Custom c++ Class to Blender and drawing it in the viewport using an Addonbpy_struct for accessing key indentifiers in a keyvalue pair(dict) in a menuWhat is the appropriate way to write and distribute an addon that works with 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 13 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
12 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
12 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
12 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 12 hours ago
batFINGERbatFINGER
25k42674
25k42674
$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
12 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
12 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
12 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
12 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 13 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 11 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 2 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