pattern_model.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. from PySide6.QtCore import QAbstractListModel, Qt, Slot, Signal
  2. from PySide6.QtQml import QmlElement
  3. from pathlib import Path
  4. QML_IMPORT_NAME = "DuneWeaver"
  5. QML_IMPORT_MAJOR_VERSION = 1
  6. @QmlElement
  7. class PatternModel(QAbstractListModel):
  8. """Model for pattern list with direct file system access"""
  9. NameRole = Qt.UserRole + 1
  10. PathRole = Qt.UserRole + 2
  11. PreviewRole = Qt.UserRole + 3
  12. def __init__(self):
  13. super().__init__()
  14. self._patterns = []
  15. self._filtered_patterns = []
  16. # Look for patterns in the parent directory (main dune-weaver folder)
  17. self.patterns_dir = Path("../patterns")
  18. self.cache_dir = Path("../patterns/cached_images")
  19. self.refresh()
  20. def roleNames(self):
  21. return {
  22. self.NameRole: b"name",
  23. self.PathRole: b"path",
  24. self.PreviewRole: b"preview"
  25. }
  26. def rowCount(self, parent=None):
  27. return len(self._filtered_patterns)
  28. def data(self, index, role):
  29. if not index.isValid() or index.row() >= len(self._filtered_patterns):
  30. return None
  31. pattern = self._filtered_patterns[index.row()]
  32. if role == self.NameRole:
  33. return pattern["name"]
  34. elif role == self.PathRole:
  35. return pattern["path"]
  36. elif role == self.PreviewRole:
  37. # For patterns in subdirectories, check both flattened and hierarchical cache structures
  38. pattern_name = pattern["name"]
  39. # Try PNG format for kiosk compatibility
  40. # First try hierarchical structure (preserving subdirectories)
  41. preview_path_hierarchical = self.cache_dir / f"{pattern_name}.png"
  42. if preview_path_hierarchical.exists():
  43. return str(preview_path_hierarchical.absolute())
  44. # Then try flattened structure (replace / with _)
  45. preview_name_flat = pattern_name.replace("/", "_").replace("\\", "_")
  46. preview_path_flat = self.cache_dir / f"{preview_name_flat}.png"
  47. if preview_path_flat.exists():
  48. return str(preview_path_flat.absolute())
  49. # Fallback to WebP if PNG not found (for existing caches)
  50. preview_path_hierarchical_webp = self.cache_dir / f"{pattern_name}.webp"
  51. if preview_path_hierarchical_webp.exists():
  52. return str(preview_path_hierarchical_webp.absolute())
  53. preview_path_flat_webp = self.cache_dir / f"{preview_name_flat}.webp"
  54. if preview_path_flat_webp.exists():
  55. return str(preview_path_flat_webp.absolute())
  56. return ""
  57. return None
  58. @Slot()
  59. def refresh(self):
  60. print(f"Loading patterns from: {self.patterns_dir.absolute()}")
  61. self.beginResetModel()
  62. patterns = []
  63. for file_path in self.patterns_dir.rglob("*.thr"):
  64. relative = file_path.relative_to(self.patterns_dir)
  65. patterns.append({
  66. "name": str(relative),
  67. "path": str(file_path)
  68. })
  69. self._patterns = sorted(patterns, key=lambda x: x["name"])
  70. self._filtered_patterns = self._patterns.copy()
  71. print(f"Loaded {len(self._patterns)} patterns")
  72. self.endResetModel()
  73. @Slot(str)
  74. def filter(self, search_text):
  75. self.beginResetModel()
  76. if not search_text:
  77. self._filtered_patterns = self._patterns.copy()
  78. else:
  79. search_lower = search_text.lower()
  80. self._filtered_patterns = [
  81. p for p in self._patterns
  82. if search_lower in p["name"].lower()
  83. ]
  84. self.endResetModel()