# treasuryOperations.py import os import shutil import sys from PySide6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget, QFileDialog, QMessageBox, QScrollArea, QGridLayout, QLabel, QPushButton, QCheckBox, QStyle) from PySide6.QtCore import Qt, QSize from PySide6.QtGui import QPixmap, QKeyEvent class AssetCardWidget(QWidget): """Visual asset card component rendering a selective thumbnail and checkbox interface.""" def __init__(self, file_path, is_video=False, parent=None): super().__init__(parent) self.file_path = file_path self.is_video = is_video self.init_card_layout() def init_card_layout(self): layout = QVBoxLayout(self) layout.setContentsMargins(5, 5, 5, 5) layout.setSpacing(6) # Fixed spatial geometry allocation for asset display cards self.setFixedSize(140, 170) self.setStyleSheet("border: 1px solid #CCCCCC; background-color: #FAFAFA; border-radius: 4px;") # Thumbnail Visual Layer self.thumbnail_label = QLabel() self.thumbnail_label.setFixedSize(130, 110) self.thumbnail_label.setAlignment(Qt.AlignCenter) if self.is_video: # Fallback to standard system video icon decoration for raw video streams style = QApplication.style() icon = style.standardIcon(QStyle.SP_MediaVolume) # Generic fallback symbol pixmap = icon.pixmap(QSize(64, 64)) self.thumbnail_label.setPixmap(pixmap) else: pixmap = QPixmap(self.file_path) if not pixmap.isNull(): self.thumbnail_label.setPixmap(pixmap.scaled(120, 100, Qt.KeepAspectRatio, Qt.SmoothTransformation)) else: self.thumbnail_label.setText("Broken Img") layout.addWidget(self.thumbnail_label) # Content Identification & Selection Checkbox Controls self.checkbox = QCheckBox(os.path.basename(self.file_path)) self.checkbox.setStyleSheet("border: none; font-size: 11px;") layout.addWidget(self.checkbox) def is_selected(self): return self.checkbox.isChecked() def set_selected(self, state): self.checkbox.setChecked(state) class CarlaTreasuryOperationsWindow(QDialog): """Centralized asset control panel handling dynamic multi-selection, file system ingestion, and media visualization.""" def __init__(self, parent=None): super().__init__(parent) self.assets_dir = os.path.abspath("assets") self.videos_dir = os.path.abspath("mp4_videos") # Verify local operational target paths exist cleanly on disk os.makedirs(self.assets_dir, exist_ok=True) os.makedirs(self.videos_dir, exist_ok=True) self.setWindowTitle("Carla Core Treasury - Asset Vault Operations") self.resize(850, 650) self.image_cards = [] self.video_cards = [] self.init_ui_matrix() self.refresh_vault_display_matrix() def init_ui_matrix(self): main_layout = QVBoxLayout(self) main_layout.setSpacing(10) main_layout.setContentsMargins(15, 15, 15, 15) # Global Control Manipulation Layer top_bar = QHBoxLayout() self.btn_select_all = QPushButton("Select All items") self.btn_deselect_all = QPushButton("Clear Selection") self.btn_add_asset = QPushButton("📥 Ingest New Asset") self.btn_remove_selected = QPushButton("🗑 Delete Selected") self.btn_add_asset.setStyleSheet("font-weight: bold; background-color: #E91E63; color: white;") self.btn_remove_selected.setStyleSheet("background-color: #D32F2F; color: white;") self.btn_select_all.clicked.connect(lambda: self.toggle_global_selection_state(True)) self.btn_deselect_all.clicked.connect(lambda: self.toggle_global_selection_state(False)) self.btn_add_asset.clicked.connect(self.handle_asset_ingestion) self.btn_remove_selected.clicked.connect(self.handle_asset_removal_sequence) top_bar.addWidget(self.btn_select_all) top_bar.addWidget(self.btn_deselect_all) top_bar.addStretch() top_bar.addWidget(self.btn_add_asset) top_bar.addWidget(self.btn_remove_selected) main_layout.addLayout(top_bar) # Layout Segmentation Management Tabs self.tab_widget = QTabWidget() # Tab A: Image Assets Subsection Grid self.image_scroll = QScrollArea() self.image_scroll.setWidgetResizable(True) self.image_container = QWidget() self.image_grid = QGridLayout(self.image_container) self.image_grid.setSpacing(10) self.image_scroll.setWidget(self.image_container) # Tab B: Video Assets Subsection Grid self.video_scroll = QScrollArea() self.video_scroll.setWidgetResizable(True) self.video_container = QWidget() self.video_grid = QGridLayout(self.video_container) self.video_grid.setSpacing(10) self.video_scroll.setWidget(self.video_container) self.tab_widget.addTab(self.image_scroll, "Image Core Library") self.tab_widget.addTab(self.video_scroll, "Video Playlist Core") main_layout.addWidget(self.tab_widget) # Bottom Functional Exit Layer bottom_bar = QHBoxLayout() self.status_label = QLabel("Vault Active | [F11: Fullscreen Mode Switch]") self.status_label.setStyleSheet("color: #757575; font-size: 11px;") self.btn_close = QPushButton("Close Asset Vault") self.btn_close.setFixedWidth(150) self.btn_close.clicked.connect(self.accept) # Triggers standard safe dialog termination loop bottom_bar.addWidget(self.status_label) bottom_bar.addStretch() bottom_bar.addWidget(self.btn_close) main_layout.addLayout(bottom_bar) def keyPressEvent(self, event: QKeyEvent): if event.key() == Qt.Key_F11: if self.isFullScreen(): self.showNormal() self.status_label.setText("Vault Active | [F11: Fullscreen Mode Switch]") else: self.showFullScreen() self.status_label.setText("Vault Active (Fullscreen Mode Enabled) | [F11: Window Mode Switch]") event.accept() else: super().keyPressEvent(event) def toggle_global_selection_state(self, state): current_tab = self.tab_widget.currentIndex() target_list = self.image_cards if current_tab == 0 else self.video_cards for card in target_list: card.set_selected(state) def clear_grid_layout_nodes(self, grid): while grid.count(): item = grid.takeAt(0) widget = item.widget() if widget is not None: widget.deleteLater() def refresh_vault_display_matrix(self): """Scans physical path points, flushes historical UI nodes, and re-renders active elements.""" self.clear_grid_layout_nodes(self.image_grid) self.clear_grid_layout_nodes(self.video_grid) self.image_cards.clear() self.video_cards.clear() # 1. Populate Image Assets matching directory schema maps img_extensions = ('.png', '.jpg', '.jpeg', '.webp') col_limit = 5 img_idx = 0 for root, _, files in os.walk(self.assets_dir): for file in files: if file.lower().endswith(img_extensions): full_path = os.path.join(root, file) card = AssetCardWidget(full_path, is_video=False) self.image_cards.append(card) self.image_grid.addWidget(card, img_idx // col_limit, img_idx % col_limit) img_idx += 1 # 2. Populate Video Assets inside target repository pathing vid_extensions = ('.mp4', '.avi', '.mkv', '.mov') vid_idx = 0 for file in os.listdir(self.videos_dir): if file.lower().endswith(vid_extensions): full_path = os.path.join(self.videos_dir, file) card = AssetCardWidget(full_path, is_video=True) self.video_cards.append(card) self.video_grid.addWidget(card, vid_idx // col_limit, vid_idx % col_limit) vid_idx += 1 def handle_asset_ingestion(self): """Dispatches operational file exploration system hooks to cleanly duplicate items into workspace paths.""" current_tab = self.tab_widget.currentIndex() if current_tab == 0: file_filter = "Images (*.png *.jpg *.jpeg *.webp)" # Prompt context tracking targets directory root subdirs = [d for d in os.listdir(self.assets_dir) if os.path.isdir(os.path.join(self.assets_dir, d))] if not subdirs: QMessageBox.warning(self, "Path Ambiguity", "Please ensure at least one asset theme directory subfolder exists.") return # Select target sub-repository mapping target target_sub, ok = QFileDialog.getOpenFileName(self, "Select Image Asset to Ingest", "", file_filter) if ok and target_sub: # Prompt user for folder placement strategy selection block from PySide6.QtWidgets import QInputDialog chosen_dir, folder_ok = QInputDialog.getItem(self, "Target Folder Assignment", "Assign to Asset Theme Subfolder:", subdirs, 0, False) if folder_ok and chosen_dir: destination = os.path.join(self.assets_dir, chosen_dir, os.path.basename(target_sub)) try: shutil.copy2(target_sub, destination) self.refresh_vault_display_matrix() except Exception as e: QMessageBox.critical(self, "Write Error", f"Asset ingestion failed: {e}") else: file_filter = "Videos (*.mp4 *.avi *.mkv *.mov)" target_vid, ok = QFileDialog.getOpenFileName(self, "Select Video Asset to Ingest", "", file_filter) if ok and target_vid: destination = os.path.join(self.videos_dir, os.path.basename(target_vid)) try: shutil.copy2(target_vid, destination) self.refresh_vault_display_matrix() except Exception as e: QMessageBox.critical(self, "Write Error", f"Asset ingestion failed: {e}") def handle_asset_removal_sequence(self): """Evaluates active check states, requests confirmation, and deletes items from disk.""" current_tab = self.tab_widget.currentIndex() target_list = self.image_cards if current_tab == 0 else self.video_cards selected_cards = [card for card in target_list if card.is_selected()] if not selected_cards: QMessageBox.information(self, "Zero Action Bounds", "Please check at least one asset box for deletion.") return confirm = QMessageBox.question( self, "Destructive Action Confirmation", f"Are you sure you want to delete the {len(selected_cards)} selected media assets from local storage?", QMessageBox.Yes | QMessageBox.No ) if confirm == QMessageBox.Yes: failures = [] for card in selected_cards: try: if os.path.exists(card.file_path): os.remove(card.file_path) except Exception as e: failures.append(f"{os.path.basename(card.file_path)}: {str(e)}") if failures: QMessageBox.warning(self, "Partial Operational Disconnect", "Some files could not be cleared:\n" + "\n".join(failures)) self.refresh_vault_display_matrix()