wip
This commit is contained in:
BIN
Archive.zip
BIN
Archive.zip
Binary file not shown.
288
__init__.py
288
__init__.py
@@ -499,7 +499,7 @@ def delete_blend_file_preset(preset_name):
|
||||
return False
|
||||
|
||||
def initialize_presets():
|
||||
"""Initialize presets during addon registration - prevents race conditions"""
|
||||
"""Initialize presets with simplified, reliable synchronization"""
|
||||
try:
|
||||
import bpy
|
||||
if not hasattr(bpy.context, 'scene') or not bpy.context.scene:
|
||||
@@ -509,113 +509,157 @@ def initialize_presets():
|
||||
if not props:
|
||||
return
|
||||
|
||||
# Check if migration is needed first
|
||||
migration_result = migrate_presets_if_needed()
|
||||
print(f"[TTG] Starting preset initialization...")
|
||||
|
||||
# Show migration notification if there were significant changes
|
||||
if migration_result.get('migrated_count', 0) > 0 or migration_result.get('errors'):
|
||||
def show_migration_notification():
|
||||
# Use a timer to show notification after UI is ready
|
||||
# Clear existing presets to avoid duplicates
|
||||
props.presets.clear()
|
||||
|
||||
# Collect all presets from all sources
|
||||
all_presets = {}
|
||||
|
||||
# 1. Load from blend file (highest priority - travels with file)
|
||||
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:
|
||||
print(f"[TTG] 🎉 MIGRATION SUCCESS: {migrated_count} presets migrated to persistent storage!")
|
||||
print(f"[TTG] 🛡️ Your presets are now protected from addon updates.")
|
||||
|
||||
if errors:
|
||||
print(f"[TTG] ⚠️ WARNING: {len(errors)} presets had migration errors.")
|
||||
print(f"[TTG] Use 'Migration Report' button in Presets section for details.")
|
||||
except Exception as e:
|
||||
print(f"[TTG] Could not show migration notification: {e}")
|
||||
return None # Stop timer
|
||||
|
||||
# Schedule notification
|
||||
bpy.app.timers.register(show_migration_notification, first_interval=2.0)
|
||||
|
||||
# Get existing preset names for optimization
|
||||
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'
|
||||
for name, data in blend_presets.items():
|
||||
all_presets[name] = {
|
||||
'data': data,
|
||||
'source': 'BLEND_FILE'
|
||||
}
|
||||
print(f"[TTG] Found {len(blend_presets)} presets in blend file")
|
||||
except Exception as e:
|
||||
print(f"[TTG] Error loading blend file presets: {e}")
|
||||
|
||||
# 2. Load from persistent storage (survives addon updates)
|
||||
try:
|
||||
persistent_dir = get_persistent_preset_path()
|
||||
if os.path.exists(persistent_dir):
|
||||
try:
|
||||
for filename in os.listdir(persistent_dir):
|
||||
if filename.endswith('.json'):
|
||||
preset_name = os.path.splitext(filename)[0]
|
||||
# Only add if not already present from blend file
|
||||
if preset_name not in blend_presets:
|
||||
updated_presets.add(preset_name)
|
||||
if preset_name in existing_presets:
|
||||
# Update existing preset source if needed
|
||||
if existing_presets[preset_name].source != 'PERSISTENT_FILE':
|
||||
existing_presets[preset_name].source = 'PERSISTENT_FILE'
|
||||
else:
|
||||
# Add new preset
|
||||
preset = props.presets.add()
|
||||
preset.name = preset_name
|
||||
preset.source = 'PERSISTENT_FILE'
|
||||
print(f"[TTG] Loaded persistent presets from {persistent_dir}")
|
||||
except OSError as e:
|
||||
print(f"[TTG] Error loading persistent presets during initialization: {e}")
|
||||
|
||||
# 3. Load legacy local presets as fallback (lowest priority)
|
||||
legacy_dir = get_preset_path()
|
||||
if os.path.exists(legacy_dir) and legacy_dir != persistent_dir:
|
||||
# Only add if not already in blend file (blend file has priority)
|
||||
if preset_name not in all_presets:
|
||||
try:
|
||||
with open(os.path.join(persistent_dir, filename), 'r') as f:
|
||||
preset_data = json.load(f)
|
||||
all_presets[preset_name] = {
|
||||
'data': preset_data,
|
||||
'source': 'PERSISTENT_FILE'
|
||||
}
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
print(f"[TTG] Error reading persistent preset {preset_name}: {e}")
|
||||
print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'PERSISTENT_FILE'])} persistent presets")
|
||||
except Exception as e:
|
||||
print(f"[TTG] Error loading persistent presets: {e}")
|
||||
|
||||
# 3. Load from legacy storage (backward compatibility)
|
||||
try:
|
||||
legacy_dir = get_preset_path()
|
||||
if os.path.exists(legacy_dir) and legacy_dir != get_persistent_preset_path():
|
||||
for filename in os.listdir(legacy_dir):
|
||||
if filename.endswith('.json'):
|
||||
preset_name = os.path.splitext(filename)[0]
|
||||
# Only add if not already present from higher priority sources
|
||||
if preset_name not in blend_presets and preset_name not in updated_presets:
|
||||
updated_presets.add(preset_name)
|
||||
if preset_name in existing_presets:
|
||||
# Update existing preset source if needed
|
||||
if existing_presets[preset_name].source != 'LOCAL_FILE':
|
||||
existing_presets[preset_name].source = 'LOCAL_FILE'
|
||||
else:
|
||||
# Add new preset
|
||||
# Only add if not already found in higher priority sources
|
||||
if preset_name not in all_presets:
|
||||
try:
|
||||
with open(os.path.join(legacy_dir, filename), 'r') as f:
|
||||
preset_data = json.load(f)
|
||||
all_presets[preset_name] = {
|
||||
'data': preset_data,
|
||||
'source': 'LOCAL_FILE'
|
||||
}
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
print(f"[TTG] Error reading legacy preset {preset_name}: {e}")
|
||||
print(f"[TTG] Found {len([p for p in all_presets.values() if p['source'] == 'LOCAL_FILE'])} legacy presets")
|
||||
except Exception as e:
|
||||
print(f"[TTG] Error loading legacy presets: {e}")
|
||||
|
||||
# Add all presets to UI list
|
||||
for preset_name, preset_info in all_presets.items():
|
||||
preset = props.presets.add()
|
||||
preset.name = preset_name
|
||||
preset.source = 'LOCAL_FILE'
|
||||
print(f"[TTG] Loaded legacy presets from {legacy_dir}")
|
||||
except OSError as e:
|
||||
print(f"[TTG] Error loading legacy presets during initialization: {e}")
|
||||
preset.source = preset_info['source']
|
||||
|
||||
# Remove presets that no longer exist
|
||||
for i in range(len(props.presets) - 1, -1, -1):
|
||||
if props.presets[i].name not in updated_presets:
|
||||
props.presets.remove(i)
|
||||
|
||||
# Count presets by source for reporting
|
||||
# Report results
|
||||
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'])
|
||||
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:
|
||||
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
|
||||
@@ -4903,6 +4947,11 @@ class TEXT_TEXTURE_OT_load_preset(Operator):
|
||||
def execute(self, context):
|
||||
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
|
||||
print(f"[PRESET LOAD DEBUG] ==================== LOADING PRESET: '{self.preset_name}' ====================")
|
||||
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):
|
||||
try:
|
||||
# Reload presets
|
||||
initialize_presets()
|
||||
# Use the reliable synchronous refresh system
|
||||
success = refresh_presets_sync()
|
||||
|
||||
if success:
|
||||
props = context.scene.text_texture_props
|
||||
blend_presets = get_blend_file_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
|
||||
for area in context.screen.areas:
|
||||
@@ -5259,6 +5313,8 @@ class TEXT_TEXTURE_OT_refresh_presets(Operator):
|
||||
|
||||
except Exception as e:
|
||||
self.report({'ERROR'}, f"Failed to refresh presets: {str(e)}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {'CANCELLED'}
|
||||
|
||||
class TEXT_TEXTURE_OT_add_overlay(Operator):
|
||||
@@ -5657,6 +5713,15 @@ class TEXT_TEXTURE_OT_duplicate_overlay(Operator):
|
||||
new_overlay.y_position = source_overlay.y_position
|
||||
new_overlay.scale = source_overlay.scale
|
||||
new_overlay.rotation = source_overlay.rotation
|
||||
# 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.text_spacing = source_overlay.text_spacing
|
||||
@@ -6841,14 +6906,25 @@ def register():
|
||||
if hasattr(bpy.types, 'IMAGE_MT_image'):
|
||||
bpy.types.IMAGE_MT_image.append(menu_func_image)
|
||||
|
||||
# Initialize presets after registration to prevent race conditions
|
||||
# Use a timer to ensure the scene is ready
|
||||
def delayed_preset_init():
|
||||
initialize_presets()
|
||||
return None # Stop timer
|
||||
# Initialize presets using reliable synchronization - no timer chains needed
|
||||
print("[TTG DEBUG] Registration completed - initializing presets with reliable system")
|
||||
|
||||
print("[TTG DEBUG] Registration completed - scheduling preset initialization")
|
||||
bpy.app.timers.register(delayed_preset_init, first_interval=0.1)
|
||||
# Use the new reliable initialization system
|
||||
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
|
||||
@bpy.app.handlers.persistent
|
||||
@@ -6856,12 +6932,18 @@ def register():
|
||||
"""Reload presets when a blend file is loaded"""
|
||||
try:
|
||||
print("[TTG] Reloading presets after file load...")
|
||||
def delayed_reload():
|
||||
initialize_presets()
|
||||
return None # Stop timer
|
||||
bpy.app.timers.register(delayed_reload, first_interval=0.1)
|
||||
|
||||
# Use reliable synchronous refresh instead of timer
|
||||
success = refresh_presets_sync()
|
||||
if success:
|
||||
print("[TTG] Presets reloaded successfully after file load")
|
||||
else:
|
||||
print("[TTG] Warning: Preset reload failed after file load")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[TTG] Error reloading presets: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Register the file load handler
|
||||
if on_file_load not in bpy.app.handlers.load_post:
|
||||
|
||||
Reference in New Issue
Block a user