wip
This commit is contained in:
BIN
Archive.zip
BIN
Archive.zip
Binary file not shown.
300
__init__.py
300
__init__.py
@@ -499,7 +499,7 @@ def delete_blend_file_preset(preset_name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def initialize_presets():
|
def initialize_presets():
|
||||||
"""Initialize presets during addon registration - prevents race conditions"""
|
"""Initialize presets with simplified, reliable synchronization"""
|
||||||
try:
|
try:
|
||||||
import bpy
|
import bpy
|
||||||
if not hasattr(bpy.context, 'scene') or not bpy.context.scene:
|
if not hasattr(bpy.context, 'scene') or not bpy.context.scene:
|
||||||
@@ -509,113 +509,157 @@ def initialize_presets():
|
|||||||
if not props:
|
if not props:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Check if migration is needed first
|
print(f"[TTG] Starting preset initialization...")
|
||||||
migration_result = migrate_presets_if_needed()
|
|
||||||
|
|
||||||
# Show migration notification if there were significant changes
|
# Clear existing presets to avoid duplicates
|
||||||
if migration_result.get('migrated_count', 0) > 0 or migration_result.get('errors'):
|
props.presets.clear()
|
||||||
def show_migration_notification():
|
|
||||||
# Use a timer to show notification after UI is ready
|
|
||||||
try:
|
|
||||||
import bpy
|
|
||||||
if hasattr(bpy.context, 'window_manager'):
|
|
||||||
migrated_count = migration_result.get('migrated_count', 0)
|
|
||||||
errors = migration_result.get('errors', [])
|
|
||||||
|
|
||||||
if migrated_count > 0:
|
# Collect all presets from all sources
|
||||||
print(f"[TTG] 🎉 MIGRATION SUCCESS: {migrated_count} presets migrated to persistent storage!")
|
all_presets = {}
|
||||||
print(f"[TTG] 🛡️ Your presets are now protected from addon updates.")
|
|
||||||
|
|
||||||
if errors:
|
# 1. Load from blend file (highest priority - travels with file)
|
||||||
print(f"[TTG] ⚠️ WARNING: {len(errors)} presets had migration errors.")
|
try:
|
||||||
print(f"[TTG] Use 'Migration Report' button in Presets section for details.")
|
blend_presets = get_blend_file_presets()
|
||||||
except Exception as e:
|
for name, data in blend_presets.items():
|
||||||
print(f"[TTG] Could not show migration notification: {e}")
|
all_presets[name] = {
|
||||||
return None # Stop timer
|
'data': data,
|
||||||
|
'source': 'BLEND_FILE'
|
||||||
# Schedule notification
|
}
|
||||||
bpy.app.timers.register(show_migration_notification, first_interval=2.0)
|
print(f"[TTG] Found {len(blend_presets)} presets in blend file")
|
||||||
|
except Exception as e:
|
||||||
# Get existing preset names for optimization
|
print(f"[TTG] Error loading blend file presets: {e}")
|
||||||
existing_presets = {p.name: p for p in props.presets}
|
|
||||||
|
|
||||||
# Three-tier loading priority: Blend File > Persistent File > Legacy File
|
|
||||||
|
|
||||||
# 1. Load presets from blend file first (highest priority)
|
|
||||||
blend_presets = get_blend_file_presets()
|
|
||||||
updated_presets = set()
|
|
||||||
|
|
||||||
for preset_name in blend_presets.keys():
|
|
||||||
updated_presets.add(preset_name)
|
|
||||||
if preset_name in existing_presets:
|
|
||||||
# Update existing preset source if needed
|
|
||||||
if existing_presets[preset_name].source != 'BLEND_FILE':
|
|
||||||
existing_presets[preset_name].source = 'BLEND_FILE'
|
|
||||||
else:
|
|
||||||
# Add new preset
|
|
||||||
preset = props.presets.add()
|
|
||||||
preset.name = preset_name
|
|
||||||
preset.source = 'BLEND_FILE'
|
|
||||||
|
|
||||||
# 2. Load from persistent storage (survives addon updates)
|
# 2. Load from persistent storage (survives addon updates)
|
||||||
persistent_dir = get_persistent_preset_path()
|
try:
|
||||||
if os.path.exists(persistent_dir):
|
persistent_dir = get_persistent_preset_path()
|
||||||
try:
|
if os.path.exists(persistent_dir):
|
||||||
for filename in os.listdir(persistent_dir):
|
for filename in os.listdir(persistent_dir):
|
||||||
if filename.endswith('.json'):
|
if filename.endswith('.json'):
|
||||||
preset_name = os.path.splitext(filename)[0]
|
preset_name = os.path.splitext(filename)[0]
|
||||||
# Only add if not already present from blend file
|
# Only add if not already in blend file (blend file has priority)
|
||||||
if preset_name not in blend_presets:
|
if preset_name not in all_presets:
|
||||||
updated_presets.add(preset_name)
|
try:
|
||||||
if preset_name in existing_presets:
|
with open(os.path.join(persistent_dir, filename), 'r') as f:
|
||||||
# Update existing preset source if needed
|
preset_data = json.load(f)
|
||||||
if existing_presets[preset_name].source != 'PERSISTENT_FILE':
|
all_presets[preset_name] = {
|
||||||
existing_presets[preset_name].source = 'PERSISTENT_FILE'
|
'data': preset_data,
|
||||||
else:
|
'source': 'PERSISTENT_FILE'
|
||||||
# Add new preset
|
}
|
||||||
preset = props.presets.add()
|
except (json.JSONDecodeError, OSError) as e:
|
||||||
preset.name = preset_name
|
print(f"[TTG] Error reading persistent preset {preset_name}: {e}")
|
||||||
preset.source = 'PERSISTENT_FILE'
|
print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'PERSISTENT_FILE'])} persistent presets")
|
||||||
print(f"[TTG] Loaded persistent presets from {persistent_dir}")
|
except Exception as e:
|
||||||
except OSError as e:
|
print(f"[TTG] Error loading persistent presets: {e}")
|
||||||
print(f"[TTG] Error loading persistent presets during initialization: {e}")
|
|
||||||
|
|
||||||
# 3. Load legacy local presets as fallback (lowest priority)
|
# 3. Load from legacy storage (backward compatibility)
|
||||||
legacy_dir = get_preset_path()
|
try:
|
||||||
if os.path.exists(legacy_dir) and legacy_dir != persistent_dir:
|
legacy_dir = get_preset_path()
|
||||||
try:
|
if os.path.exists(legacy_dir) and legacy_dir != get_persistent_preset_path():
|
||||||
for filename in os.listdir(legacy_dir):
|
for filename in os.listdir(legacy_dir):
|
||||||
if filename.endswith('.json'):
|
if filename.endswith('.json'):
|
||||||
preset_name = os.path.splitext(filename)[0]
|
preset_name = os.path.splitext(filename)[0]
|
||||||
# Only add if not already present from higher priority sources
|
# Only add if not already found in higher priority sources
|
||||||
if preset_name not in blend_presets and preset_name not in updated_presets:
|
if preset_name not in all_presets:
|
||||||
updated_presets.add(preset_name)
|
try:
|
||||||
if preset_name in existing_presets:
|
with open(os.path.join(legacy_dir, filename), 'r') as f:
|
||||||
# Update existing preset source if needed
|
preset_data = json.load(f)
|
||||||
if existing_presets[preset_name].source != 'LOCAL_FILE':
|
all_presets[preset_name] = {
|
||||||
existing_presets[preset_name].source = 'LOCAL_FILE'
|
'data': preset_data,
|
||||||
else:
|
'source': 'LOCAL_FILE'
|
||||||
# Add new preset
|
}
|
||||||
preset = props.presets.add()
|
except (json.JSONDecodeError, OSError) as e:
|
||||||
preset.name = preset_name
|
print(f"[TTG] Error reading legacy preset {preset_name}: {e}")
|
||||||
preset.source = 'LOCAL_FILE'
|
print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'LOCAL_FILE'])} legacy presets")
|
||||||
print(f"[TTG] Loaded legacy presets from {legacy_dir}")
|
except Exception as e:
|
||||||
except OSError as e:
|
print(f"[TTG] Error loading legacy presets: {e}")
|
||||||
print(f"[TTG] Error loading legacy presets during initialization: {e}")
|
|
||||||
|
|
||||||
# Remove presets that no longer exist
|
# Add all presets to UI list
|
||||||
for i in range(len(props.presets) - 1, -1, -1):
|
for preset_name, preset_info in all_presets.items():
|
||||||
if props.presets[i].name not in updated_presets:
|
preset = props.presets.add()
|
||||||
props.presets.remove(i)
|
preset.name = preset_name
|
||||||
|
preset.source = preset_info['source']
|
||||||
|
|
||||||
# Count presets by source for reporting
|
# Report results
|
||||||
blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE'])
|
blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE'])
|
||||||
persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE'])
|
persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE'])
|
||||||
legacy_count = len([p for p in props.presets if p.source == 'LOCAL_FILE'])
|
legacy_count = len([p for p in props.presets if p.source == 'LOCAL_FILE'])
|
||||||
|
|
||||||
print(f"[TTG] Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent, {legacy_count} legacy)")
|
print(f"[TTG] ✅ Initialized {len(props.presets)} presets ({blend_count} blend file, {persistent_count} persistent, {legacy_count} legacy)")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[TTG] Error in initialize_presets: {e}")
|
print(f"[TTG] Critical error in initialize_presets: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
def refresh_presets():
|
||||||
|
"""Refresh the preset list by rescanning files (legacy function)"""
|
||||||
|
import bpy
|
||||||
|
refresh_presets_sync()
|
||||||
|
|
||||||
|
# Force UI update
|
||||||
|
try:
|
||||||
|
if bpy.context.area:
|
||||||
|
bpy.context.area.tag_redraw()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def refresh_presets_sync():
|
||||||
|
"""Centralized, synchronous preset refresh - guarantees completion"""
|
||||||
|
try:
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
print("[TTG] 🔄 Synchronizing presets...")
|
||||||
|
|
||||||
|
# Ensure we have valid context
|
||||||
|
if not hasattr(bpy.context, 'scene') or not bpy.context.scene:
|
||||||
|
print("[TTG] ⚠️ No valid scene context for preset refresh")
|
||||||
|
return False
|
||||||
|
|
||||||
|
props = bpy.context.scene.text_texture_props
|
||||||
|
if not props:
|
||||||
|
print("[TTG] ⚠️ No text_texture_props found")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Run the simplified initialization
|
||||||
|
initialize_presets()
|
||||||
|
|
||||||
|
# Validate that presets are actually loaded
|
||||||
|
preset_count = len(props.presets)
|
||||||
|
if preset_count == 0:
|
||||||
|
print("[TTG] ⚠️ Warning: No presets found after refresh")
|
||||||
|
else:
|
||||||
|
print(f"[TTG] ✅ Preset sync complete - {preset_count} presets available")
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[TTG] ❌ Error in refresh_presets_sync: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
return False
|
||||||
|
|
||||||
|
def ensure_presets_available():
|
||||||
|
"""Defensive programming - ensure presets are loaded before UI operations"""
|
||||||
|
try:
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
if not hasattr(bpy.context, 'scene') or not bpy.context.scene:
|
||||||
|
return False
|
||||||
|
|
||||||
|
props = bpy.context.scene.text_texture_props
|
||||||
|
if not props:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# If no presets loaded, try to load them
|
||||||
|
if len(props.presets) == 0:
|
||||||
|
print("[TTG] 🛡️ No presets available - attempting to load...")
|
||||||
|
return refresh_presets_sync()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[TTG] Error in ensure_presets_available: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# PREVIEW GENERATION FUNCTIONS
|
# PREVIEW GENERATION FUNCTIONS
|
||||||
@@ -4903,6 +4947,11 @@ class TEXT_TEXTURE_OT_load_preset(Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
props = context.scene.text_texture_props
|
props = context.scene.text_texture_props
|
||||||
|
|
||||||
|
# DEFENSIVE PROGRAMMING: Ensure presets are available before loading
|
||||||
|
if not ensure_presets_available():
|
||||||
|
self.report({'ERROR'}, "Failed to ensure presets are loaded - cannot load preset")
|
||||||
|
return {'CANCELLED'}
|
||||||
|
|
||||||
# DETAILED LOGGING: Current shader properties before loading
|
# DETAILED LOGGING: Current shader properties before loading
|
||||||
print(f"[PRESET LOAD DEBUG] ==================== LOADING PRESET: '{self.preset_name}' ====================")
|
print(f"[PRESET LOAD DEBUG] ==================== LOADING PRESET: '{self.preset_name}' ====================")
|
||||||
print(f"[PRESET LOAD DEBUG] Current shader properties BEFORE loading:")
|
print(f"[PRESET LOAD DEBUG] Current shader properties BEFORE loading:")
|
||||||
@@ -5240,16 +5289,21 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator):
|
|||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
try:
|
try:
|
||||||
# Reload presets
|
# Use the reliable synchronous refresh system
|
||||||
initialize_presets()
|
success = refresh_presets_sync()
|
||||||
|
|
||||||
props = context.scene.text_texture_props
|
if success:
|
||||||
blend_presets = get_blend_file_presets()
|
props = context.scene.text_texture_props
|
||||||
total_presets = len(props.presets)
|
total_presets = len(props.presets)
|
||||||
blend_count = len(blend_presets)
|
|
||||||
local_count = total_presets - blend_count
|
|
||||||
|
|
||||||
self.report({'INFO'}, f"✅ Refreshed {total_presets} presets ({blend_count} from blend file, {local_count} local)")
|
# Count by source for detailed reporting
|
||||||
|
blend_count = len([p for p in props.presets if p.source == 'BLEND_FILE'])
|
||||||
|
persistent_count = len([p for p in props.presets if p.source == 'PERSISTENT_FILE'])
|
||||||
|
local_count = len([p for p in props.presets if p.source == 'LOCAL_FILE'])
|
||||||
|
|
||||||
|
self.report({'INFO'}, f"✅ Refreshed {total_presets} presets ({blend_count} blend file, {persistent_count} persistent, {local_count} local)")
|
||||||
|
else:
|
||||||
|
self.report({'WARNING'}, "Preset refresh completed with issues - check console for details")
|
||||||
|
|
||||||
# Force UI redraw
|
# Force UI redraw
|
||||||
for area in context.screen.areas:
|
for area in context.screen.areas:
|
||||||
@@ -5259,6 +5313,8 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}")
|
self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
|
|
||||||
class TEXT_TEXTURE_OT_add_overlay(Operator):
|
class TEXT_TEXTURE_OT_add_overlay(Operator):
|
||||||
@@ -5657,7 +5713,16 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator):
|
|||||||
new_overlay.y_position = source_overlay.y_position
|
new_overlay.y_position = source_overlay.y_position
|
||||||
new_overlay.scale = source_overlay.scale
|
new_overlay.scale = source_overlay.scale
|
||||||
new_overlay.rotation = source_overlay.rotation
|
new_overlay.rotation = source_overlay.rotation
|
||||||
new_overlay.z_index = source_overlay.z_index
|
# Fix z-index assignment for duplicated overlays to ensure proper visibility
|
||||||
|
# For PREPEND/APPEND overlays, increment z_index to ensure duplicate appears above original
|
||||||
|
# This prevents the duplicate from being rendered behind the original overlay
|
||||||
|
if source_overlay.positioning_mode in ['PREPEND', 'APPEND']:
|
||||||
|
# Increment z_index to ensure proper layering order for duplicated overlay
|
||||||
|
new_overlay.z_index = min(source_overlay.z_index + 1, 10) # Cap at max z_index value
|
||||||
|
print(f"[DUPLICATE DEBUG] Fixed z_index for {source_overlay.positioning_mode} overlay: {source_overlay.z_index} -> {new_overlay.z_index}")
|
||||||
|
else:
|
||||||
|
# For ABSOLUTE overlays, keep same z_index (different positions so no overlap issue)
|
||||||
|
new_overlay.z_index = source_overlay.z_index
|
||||||
new_overlay.positioning_mode = source_overlay.positioning_mode
|
new_overlay.positioning_mode = source_overlay.positioning_mode
|
||||||
new_overlay.text_spacing = source_overlay.text_spacing
|
new_overlay.text_spacing = source_overlay.text_spacing
|
||||||
new_overlay.image_spacing = source_overlay.image_spacing
|
new_overlay.image_spacing = source_overlay.image_spacing
|
||||||
@@ -6841,14 +6906,25 @@ def register():
|
|||||||
if hasattr(bpy.types, 'IMAGE_MT_image'):
|
if hasattr(bpy.types, 'IMAGE_MT_image'):
|
||||||
bpy.types.IMAGE_MT_image.append(menu_func_image)
|
bpy.types.IMAGE_MT_image.append(menu_func_image)
|
||||||
|
|
||||||
# Initialize presets after registration to prevent race conditions
|
# Initialize presets using reliable synchronization - no timer chains needed
|
||||||
# Use a timer to ensure the scene is ready
|
print("[TTG DEBUG] Registration completed - initializing presets with reliable system")
|
||||||
def delayed_preset_init():
|
|
||||||
initialize_presets()
|
|
||||||
return None # Stop timer
|
|
||||||
|
|
||||||
print("[TTG DEBUG] Registration completed - scheduling preset initialization")
|
# Use the new reliable initialization system
|
||||||
bpy.app.timers.register(delayed_preset_init, first_interval=0.1)
|
try:
|
||||||
|
# Perform migration check first
|
||||||
|
migration_result = migrate_presets_if_needed()
|
||||||
|
if migration_result['migrated_count'] > 0:
|
||||||
|
print(f"[TTG DEBUG] Migrated {migration_result['migrated_count']} presets during registration")
|
||||||
|
|
||||||
|
# Initialize presets using the simplified system
|
||||||
|
initialize_presets()
|
||||||
|
print("[TTG DEBUG] Preset initialization completed successfully")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[TTG DEBUG] Error during preset initialization: {e}")
|
||||||
|
# Don't fail registration if preset initialization fails
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
# Add handler to reload presets when blend file changes
|
# Add handler to reload presets when blend file changes
|
||||||
@bpy.app.handlers.persistent
|
@bpy.app.handlers.persistent
|
||||||
@@ -6856,12 +6932,18 @@ def register():
|
|||||||
"""Reload presets when a blend file is loaded"""
|
"""Reload presets when a blend file is loaded"""
|
||||||
try:
|
try:
|
||||||
print("[TTG] Reloading presets after file load...")
|
print("[TTG] Reloading presets after file load...")
|
||||||
def delayed_reload():
|
|
||||||
initialize_presets()
|
# Use reliable synchronous refresh instead of timer
|
||||||
return None # Stop timer
|
success = refresh_presets_sync()
|
||||||
bpy.app.timers.register(delayed_reload, first_interval=0.1)
|
if success:
|
||||||
|
print("[TTG] Presets reloaded successfully after file load")
|
||||||
|
else:
|
||||||
|
print("[TTG] Warning: Preset reload failed after file load")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[TTG] Error reloading presets: {e}")
|
print(f"[TTG] Error reloading presets: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
# Register the file load handler
|
# Register the file load handler
|
||||||
if on_file_load not in bpy.app.handlers.load_post:
|
if on_file_load not in bpy.app.handlers.load_post:
|
||||||
|
|||||||
Reference in New Issue
Block a user