Last active
March 3, 2025 20:31
-
-
Save Andrej730/ffe91117fea88c84f28df000463447a5 to your computer and use it in GitHub Desktop.
Blender - Automatically reload texts which has changed externally
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ***** BEGIN GPL LICENSE BLOCK ***** | |
# | |
# | |
# This program 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 2 | |
# of the License, or (at your option) any later version. | |
# | |
# This program 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 this program; if not, write to the Free Software Foundation, | |
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
# | |
# ***** END GPL LICENCE BLOCK ***** | |
bl_info = { | |
"name": "Reload if Modified", | |
"author": "Dany Lebel (Axon_D), updated by @Andrej730", | |
"version": (1,0), | |
"blender": (2, 80, 0), | |
"location": "Text Editor -> Header Bar -> Reload if Modified", | |
"description": "Determine if a text datablock must be reloaded from file in the case that it has changed", | |
"warning": "", | |
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/Text_Editor/Reload_if_Modified", | |
"tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=25277&group_id=153&atid=467", | |
"category": "Text Editor" | |
} | |
# https://archive.blender.org/wiki/index.php/Extensions:2.5/Py/Scripts/Text_Editor/Reload_if_Modified/ | |
""" | |
This addon will automatically reload a text datablock if its source file | |
does not corresponds to the last blendfile version. This must be checked | |
for each file wanted to be updated automatically. It has no effect on internal datablock. | |
""" | |
import bpy | |
def check_texts_every_second(): | |
for text in bpy.data.texts: | |
if text.is_in_memory: | |
continue | |
if text.is_modified and text.reload_if_modified: | |
# using reloading operators causes crashes, therefore we reload the text manually | |
# saving text to file afterwards as it's the only way to flip `.is_modified` | |
# since reload doesn't work - https://projects.blender.org/blender/blender/issues/113644 | |
with open(text.filepath, 'r') as fi: | |
new_text = fi.read() | |
text.from_string(new_text) | |
def get_area(): | |
for screen in bpy.data.screens: | |
for area in screen.areas: | |
if area.type == "TEXT_EDITOR": | |
return area | |
context_override = {} | |
context_override["edit_text"] = text | |
space = next(space for space in get_area().spaces if space.type == "TEXT_EDITOR") | |
context_override["space_data"] = space | |
with bpy.context.temp_override(**context_override): | |
bpy.ops.text.save() | |
return 1 # every second | |
def draw_header(self, context): | |
text = context.space_data.text | |
if not text: | |
return | |
if text.is_in_memory: | |
return | |
layout = self.layout | |
row = layout.row() | |
row.prop(text, "reload_if_modified") | |
def register(): | |
bpy.types.Text.reload_if_modified = bpy.props.BoolProperty( | |
name='Reload if Modified', | |
description= | |
'Automatically reload text file if it has changed', | |
default=False, | |
) | |
# tried `bpy.msgbus.subscribe_rna(key=bpy.data.texts.path_resolve("is_modified", False))` | |
# and didn't worked, therefore going to timer approach as depsgraph update won't be often enough | |
# maybe there is more clever way to do this | |
bpy.types.TEXT_HT_header.append(draw_header) | |
bpy.app.timers.register(check_texts_every_second) | |
def unregister(): | |
del bpy.types.Text.reload_if_modified | |
bpy.types.TEXT_HT_header.remove(draw_header) | |
bpy.app.timers.register(check_texts_every_second) | |
if __name__ == '__main__': | |
register() |
root cause seems like this (back from 2014 lol)
Alright I made it. Change everything below draw_header()
into:
@bpy.app.handlers.persistent
def load_handler(dummy):
bpy.app.timers.register(check_texts_every_second)
def register():
# build UI once per plugin load
bpy.types.Text.reload_if_modified = bpy.props.BoolProperty(
name='Reload if Modified',
description=
'Automatically reload text file if it has changed',
default=False,
)
# tried `bpy.msgbus.subscribe_rna(key=bpy.data.texts.path_resolve("is_modified", False))`
# and didn't worked, therefore going to timer approach as depsgraph update won't be often enough
# maybe there is more clever way to do this
bpy.types.TEXT_HT_header.append(draw_header)
# register timer once per plugin load
bpy.app.timers.register(check_texts_every_second)
# loading a file cancels all timers, register timer once more per file load
bpy.app.handlers.load_post.append(load_handler)
def unregister():
del bpy.types.Text.reload_if_modified
bpy.types.TEXT_HT_header.remove(draw_header)
bpy.app.timers.unregister(check_texts_every_second)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I did not see any error messages on
blender --debug-all
, so I turned to good ol' print debugging. I injected:and restarted blender with
blender --debug-python
. I found out that, on blender restart, the heartbeat is there, meaning the scheduled event is running every second. but the moment any project file is loaded, the heartbeat disappeared, with no error messages.Then if I proceed to disable the add-on, messages come:
(Update: There is a typo on line 104,
register
should beunregister
xd. But if I made the change, error message on disabling will change into "function not registered", which indicates a same problem: Python context is reset (update update: see below, all timers are cancelled) andregister()
is not called on project load.)Then finally I re-enable the add-on,