# === Standard Library === import glob import importlib import json import os import re import shutil import subprocess import sys import webbrowser # === PyQt6 === from PyQt6.QtCore import Qt from PyQt6.QtGui import QFont, QFontDatabase, QPixmap from PyQt6.QtWidgets import ( QApplication, QFileDialog, QHBoxLayout, QLabel, QLineEdit, QMainWindow, QMessageBox, QPushButton, QPlainTextEdit, QProgressBar, QSizePolicy, QSpacerItem, QStackedLayout, QStackedWidget, QVBoxLayout, QWidget ) # === Local Modules === from worker import WorkerThread # === App Configuration === config_path = os.path.join(os.path.dirname(__file__), ".config_app") class MiVentana(QMainWindow): def __init__(self): super().__init__() self.setMinimumSize(960, 540) font_id = QFontDatabase.addApplicationFont(".config_app/urbanist.ttf") font_families = QFontDatabase.applicationFontFamilies(font_id) font = QFont(font_families[0]) QApplication.setFont(font) self.setStyleSheet(open('.config_app/layout.css', 'r').read()) self.stacked_widget = QStackedWidget(self) self.pages = {} print("Style default load") print("Font default load:", font_families) #PageImportWallet -->Page start #PageWebsites -->Page Two #PageTemplates -->Page three #PageCreateWebsite -->Page four #PagePublic -->Page five #PageWebsitesEditor -->Page six #PageEditWebsite -->Page seven for page_class in [PageImportWallet,PageWebsites,PageTemplates,PageCreateWebsite,PagePublic,PageWebsitesEditor,PageEditWebsite]: page_instance = page_class(self.stacked_widget) self.stacked_widget.addWidget(page_instance) self.pages[page_class.__name__] = page_instance self.setCentralWidget(self.stacked_widget) # Cargador de arranque -- importante! perfiles = os.listdir('.config_app/profiles') if os.path.exists('.config_app/profiles') else [] if any(os.path.isdir(os.path.join('.config_app/profiles', p)) for p in perfiles): self.stacked_widget.setCurrentWidget(self.pages["PageWebsites"]) print("Wallet Json detected") else: self.stacked_widget.setCurrentWidget(self.pages["PageImportWallet"]) print("No detected Wallet Json") print("Welcome Neo") class BasePage(QWidget): def __init__(self, stacked_widget, container_width_percentage, layout_type): super().__init__() self.stacked_widget = stacked_widget self.container_width_percentage = container_width_percentage BasePage.profile_selected = 'default' layout = QVBoxLayout(self) for object_name, widget_type, box, height in [ ('widget_up', QWidget, QHBoxLayout, 30), ('widget_center', QWidget, layout_type, None), ('widget_down', QWidget, QHBoxLayout, 35) ]: widget = widget_type() layout_widget = box() widget.setLayout(layout_widget) layout_widget.setContentsMargins(10, 0, 10, 0) widget.setObjectName(object_name) if height is not None: widget.setFixedHeight(height) setattr(self, object_name, widget) layout.addWidget(widget) #widget_up === if object_name == 'widget_up': for index, (label_name, text) in enumerate([ ('label_title', 'ArWeb'), ('label_balance', 'Balance') ]): label = QLabel(text) label.setObjectName(label_name) setattr(self, label_name, label) widget.layout().addWidget(label) if index == 0: spacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) widget.layout().addItem(spacer) #widget_center === elif object_name == 'widget_center': self.container = QWidget() self.container_layout = layout_type(self.container) self.container_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.container.setLayout(self.container_layout) self.loading_group = QWidget() loading_group_layout = QVBoxLayout(self.loading_group) for obj_name, widget_type, h in [ ('label_loading', QLabel, 20), ('progress_bar', QProgressBar, 4) ]: w = widget_type() w.setObjectName(obj_name) w.setFixedHeight(h) if isinstance(w, QLabel): w.setAlignment(Qt.AlignmentFlag.AlignCenter) w.setText("Loading...") elif isinstance(w, QProgressBar): w.setRange(0, 0) w.setTextVisible(True) setattr(self, obj_name, w) loading_group_layout.addWidget(w) self.container_layout.addWidget(self.loading_group) widget.layout().addWidget(self.container) self.loading_group.hide() # Evento resize personalizado self.widget_center.resizeEvent = self.resize_event_custom # === Zona widget_down === elif object_name == 'widget_down': self.btn_settings = QPushButton('Settings') self.btn_settings.setObjectName('button_settings') self.btn_settings.setFixedWidth(80) self.btn_settings.clicked.connect(self.settings) widget.layout().addWidget(self.btn_settings) spacer = QSpacerItem(40, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum) widget.layout().addItem(spacer) for btn_name, text, func, width in [ ('button_back', 'Back', self.go_back, 40), ('button_next', 'Next', self.go_next, 40) ]: btn = QPushButton(text) btn.setObjectName(btn_name) btn.setFixedWidth(width) btn.clicked.connect(func) setattr(self, btn_name, btn) widget.layout().addWidget(btn) self.button_back.hide() self.button_next.hide() self.setLayout(layout) def settings(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsites"]) def go_next(self): pass def go_back(self): pass def show_balance(self): try: out = subprocess.run( ["ardrive", "get-balance", "-w", f".config_app/profiles/{BasePage.profile_selected}/wallet_{BasePage.profile_selected}.json"], capture_output=True, text=True, check=True, timeout=5 ).stdout.strip().splitlines() lines = [" ".join(line.split()) for line in out if line.strip()] text = " // ".join(lines) if len(lines) == 2 else "Invalid balance format" except Exception: text = "Error getting balance" self.label_balance.setText(f"Balance: {text}") def resize_event_custom(self, event): self.container.setFixedWidth(int(self.widget_center.width() * self.container_width_percentage)) self.container.setFixedHeight(self.widget_center.height()) QWidget.resizeEvent(self.widget_center, event) def showEvent(self, event): super().showEvent(event) self.show_balance() print('Refresh balance') class PageImportWallet(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.6, QVBoxLayout) self.label_balance.hide() for object_name, widget_type, height, text, action in [ ('label_select', QLabel, 60, "Eternal Legacy allows you to create or import an Arweave wallet for your website.\n" "How would you like to proceed?", None), ('button_create', QPushButton, 40, "Create Arweave Wallet", self.create_wallet), ('button_import', QPushButton, 40, "Import Arweave Wallet", self.import_wallet), ]: widget = widget_type(text) widget.setObjectName(object_name) widget.setFixedHeight(height) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) if widget_type == QLabel: widget.setAlignment(Qt.AlignmentFlag.AlignCenter) if widget_type == QPushButton and action: widget.clicked.connect(action) setattr(self, object_name, widget) self.container_layout.addWidget(widget) self.widget_center.layout().setAlignment(Qt.AlignmentFlag.AlignCenter) self.widget_center.layout().addWidget(self.container) def create_wallet(self): for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.hide() self.loading_group.show() self.label_loading.setText("Creating wallet...") self.worker = WorkerThread('create_wallet', BasePage.profile_selected) self.worker.result.connect(self.verify_worker) self.worker.start() def import_wallet(self): base_dir = f".config_app/profiles/{BasePage.profile_selected}/" destino = os.path.join(base_dir, f"wallet_{BasePage.profile_selected}.json") def copiar_archivo(origen): if not os.path.isfile(origen): print("The selected file does not exist") return False os.makedirs(base_dir, exist_ok=True) shutil.copy(origen, destino) print(f"File copied as: {destino}") return True def verify_wallet(): for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.hide() self.loading_group.show() self.label_loading.setText("Verifying wallet...") self.worker = WorkerThread('verify_wallet', BasePage.profile_selected) self.worker.result.connect(self.verify_worker) self.worker.start() file_dialog = QFileDialog(self) if file_dialog.exec(): file_path = file_dialog.selectedFiles()[0] if copiar_archivo(file_path): verify_wallet() else: print("No file was selected") def verify_worker(self, success: bool): base_dir = f".config_app/profiles/{BasePage.profile_selected}/" destino = os.path.join(base_dir, f"wallet_{BasePage.profile_selected}.json") if success: print("Wallet verified successfully") self.go_next() else: if os.path.isfile(destino): os.remove(destino) print(f"Wallet not verified, file {destino} removed.") else: print(f"The file {destino} does not exist to be removed.") self.loading_group.hide() for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget and widget is not self.loading_group: widget.show() if success: print("Wallet verified successfully") self.go_next() else: if os.path.isfile(destino): os.remove(destino) print(f"Wallet not verified, file {destino} removed.") else: print(f"The file {destino} does not exist to be removed.") def go_next(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsites"]) class PageWebsites(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.6, QVBoxLayout) self.button_back.show() for object_name, widget_type, height, text, action in [ ('label_select', QLabel, 60, "What would you like to do?", None), ('button_edit', QPushButton, 40, "View or Edit Website", self.edit_website), ('button_new_website', QPushButton, 40, "Create New Website", self.new_website), ]: widget = widget_type(text) widget.setObjectName(object_name) widget.setFixedHeight(height) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) if widget_type == QLabel: widget.setAlignment(Qt.AlignmentFlag.AlignCenter) if widget_type == QPushButton and action: widget.clicked.connect(action) setattr(self, object_name, widget) self.container_layout.addWidget(widget) self.widget_center.layout().setAlignment(Qt.AlignmentFlag.AlignCenter) self.widget_center.layout().addWidget(self.container) def edit_website(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsitesEditor"]) def new_website(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageTemplates"]) def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageImportWallet"]) class PageTemplates(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.98, QHBoxLayout) self.button_back.show() self.container_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.container_layout.setContentsMargins(0, 0, 0, 0) def showEvent(self, event): super().showEvent(event) if not hasattr(self, "widget_rotary"): self.build_template_view() def build_template_view(self): if hasattr(self, "widget_rotary"): self.container_layout.removeWidget(self.button_before) self.container_layout.removeWidget(self.widget_rotary) self.container_layout.removeWidget(self.button_after) self.widget_rotary.deleteLater() self.button_before.deleteLater() self.button_after.deleteLater() del self.widget_rotary, self.button_before, self.button_after self.elements.clear() for name, widget_type, text, callback in [ ("button_before", QPushButton, "◀", self.go_before), ("widget_rotary", QWidget, None, ""), ("button_after", QPushButton, "▶", self.go_after), ]: widget = widget_type(text) if text else widget_type() widget.setObjectName(name) if isinstance(widget, QPushButton): widget.setFixedSize(40, 40) widget.clicked.connect(callback) widget.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) setattr(self, name, widget) else: widget.setContentsMargins(0, 0, 0, 0) self.widget_rotary_layout = QStackedLayout(widget) self.widget_rotary_layout.setContentsMargins(0, 0, 0, 0) self.widget_rotary_layout.setSpacing(0) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.widget_rotary = widget self.elements = [] self.current_index = 0 template_dir = ".config_app/templates" if os.path.exists(template_dir): for folder_name in os.listdir(template_dir): folder_path = os.path.join(template_dir, folder_name) if os.path.isdir(folder_path): for file_name in os.listdir(folder_path): if file_name.lower().endswith(".png"): label = QLabel() label.setAlignment(Qt.AlignmentFlag.AlignCenter) label.setStyleSheet("background-color: transparent;") label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) label.setPixmap(QPixmap(os.path.join(folder_path, file_name))) label.setScaledContents(True) self.elements.append((folder_name, label)) self.template_button = QPushButton("Import your template") self.template_button.clicked.connect(self.open_zip) self.template_button.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.elements.append(self.template_button) for element in self.elements: widget = element if isinstance(element, QPushButton) else element[1] self.widget_rotary_layout.addWidget(widget) self.widget_rotary_layout.setCurrentIndex(self.current_index) self.container_layout.addWidget(self.button_before) self.container_layout.addWidget(self.widget_rotary) self.container_layout.addWidget(self.button_after) layout = self.widget_center.layout() layout.setAlignment(Qt.AlignmentFlag.AlignCenter) layout.addWidget(self.container) self._update_next_button_visibility() def _update_next_button_visibility(self): current_element = self.elements[self.current_index] if isinstance(current_element, tuple): self.button_next.show() else: self.button_next.hide() def go_before(self): self.current_index = (self.current_index - 1) % len(self.elements) self.widget_rotary_layout.setCurrentIndex(self.current_index) self._update_next_button_visibility() def go_after(self): self.current_index = (self.current_index + 1) % len(self.elements) self.widget_rotary_layout.setCurrentIndex(self.current_index) self._update_next_button_visibility() def open_zip(self): try: base_path = os.path.dirname(os.path.abspath(__file__)) zip_script_path = os.path.join(base_path, ".config_app", "code", "open_zip.py") print(f"Buscando script en: {zip_script_path}") if os.path.exists(zip_script_path): print(f"Script encontrado en: {zip_script_path}") name = os.path.splitext(os.path.basename(zip_script_path))[0] print(f"Nombre del módulo: {name}") # Cargar el módulo dinámicamente spec = importlib.util.spec_from_file_location(name, zip_script_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) print("Módulo cargado correctamente.") # Verificar si la función 'open_zip' está en el módulo if hasattr(module, 'open_zip'): print("Función 'open_zip' encontrada en el módulo.") result = module.open_zip() if result: print("Template imported successfully.") self.build_template_view() else: print("Failed to import template.") else: print(f"'open_zip' function not found in {zip_script_path}") else: print(f"{zip_script_path} no encontrado.") except Exception as e: print(f"Error loading {zip_script_path}: {e}") def go_next(self): try: item = self.elements[self.current_index] if isinstance(item, QPushButton): raise Exception BasePage.template_selected = item[0] print(f"Template selected: {BasePage.template_selected}") self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageCreateWebsite"]) except Exception: QMessageBox.warning(self, "Warning", "Please select or import a template first.") def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsites"]) class PageCreateWebsite(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.98, QHBoxLayout) self.button_back.show() self._last_template_loaded = None self.conclave = {} self.hugo_memory = {} for object_name in [("widget_left"), ("widget_right")]: widget = QWidget() widget.setObjectName(object_name) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) layout = QVBoxLayout(widget) layout.setContentsMargins(0, 0, 0, 0) setattr(self, object_name, widget) setattr(self, f"{object_name}_layout", layout) self.container_layout.addWidget(widget, 1) for object_name, widget_type, text, action, height in [ ("label_website", QLabel, "General website config", None, 30), ("line_project", QLineEdit, "Project Name", None, 30), ("line_website_name", QLineEdit, "Website Name", None, 30), ("plain_website_description", QPlainTextEdit, "Website Description", None, 65), ("plain_website_seo", QPlainTextEdit, "Website Seo (separated with commas)", None, 65), ("button_favicon", QPushButton, "Select Favicon", self.favicon, 30) ]: widget = widget_type() widget.setObjectName(object_name) widget.setFixedHeight(height) if isinstance(widget, (QLabel, QPushButton)): widget.setText(text) if isinstance(widget, QPushButton) and action: widget.clicked.connect(action) self.conclave[object_name] = False if isinstance(widget, (QLineEdit, QPlainTextEdit)): widget.setPlaceholderText(text) widget.textChanged.connect(self.check_conclave) self.conclave[object_name] = False self.widget_left_layout.addWidget(widget) for icon_name, placeholder in [ ("footer_nostr", "Nostr link"), ("footer_bastyon", "Bastyon link"), ("footer_session", "Session link"), ("footer_signal", "Signal link") ]: widget = QWidget() widget.setObjectName(f"widget_footer_{icon_name}") widget.setFixedHeight(30) widget.setLayout(QHBoxLayout()) widget.layout().setContentsMargins(0, 0, 0, 0) widget.setStyleSheet("background-color: white; border: 0px; margin: 0px;") label = QLabel() label.setFixedSize(30, 30) label.setPixmap(QPixmap(f".config_app/images/{icon_name}.svg")) label.setScaledContents(True) line_edit = QLineEdit() line_edit.setFixedHeight(30) line_edit.setPlaceholderText(placeholder) line_edit.setObjectName(icon_name) # Añadido aquí widget.layout().addWidget(label) widget.setFixedHeight(30) widget.layout().addWidget(line_edit) self.widget_left_layout.addWidget(widget) self.widget_center.layout().setAlignment(Qt.AlignmentFlag.AlignCenter) self.widget_center.layout().addWidget(self.container) def showEvent(self, event): super().showEvent(event) print(f"Layout is: {BasePage.template_selected}") self.reset_widgets() if self._last_template_loaded == BasePage.template_selected: print("La plantilla ya fue cargada anteriormente, omitiendo carga de widgets.") return for i in reversed(range(self.widget_right_layout.count())): widget = self.widget_right_layout.itemAt(i).widget() if widget: widget.setParent(None) self._last_template_loaded = BasePage.template_selected template_dir = f".config_app/templates/{BasePage.template_selected}/" # Limpiar widgets anteriores for i in reversed(range(self.widget_right_layout.count())): widget = self.widget_right_layout.itemAt(i).widget() if widget: widget.setParent(None) self._last_template_loaded = BasePage.template_selected # Ruta del template seleccionado template_dir = f".config_app/templates/{BasePage.template_selected}/" print(f"Buscando archivos en: {template_dir}") # Buscar y cargar el .py for file_path in glob.glob(f"{template_dir}*.py"): print(f"Encontrado archivo Python: {file_path}") try: name = os.path.splitext(os.path.basename(file_path))[0] spec = importlib.util.spec_from_file_location(name, file_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if hasattr(module, 'add_right_widgets'): print(f"Llamando a add_right_widgets desde {file_path}") module.add_right_widgets(self) else: print(f"'add_right_widgets' no encontrado en {file_path}") except Exception as e: print(f"Error cargando {file_path} → {e}") template_dir = f".config_app/templates/{BasePage.template_selected}/" # Buscar y cargar archivos .css css_files = glob.glob(os.path.join(template_dir, "*.css")) if css_files: css_path = css_files[0] print(f"Hoja de estilo encontrada: {css_path}") try: with open(css_path, "r", encoding="utf-8") as f: self.setStyleSheet(f.read()) print(f"Estilo aplicado desde: {css_path}") except Exception as e: print(f"Error al cargar el estilo desde {css_path} → {e}") else: print(f"No se encontró ningún archivo .css en: {template_dir}") def reset_widgets(self): for container in [self.widget_left, self.widget_right]: for widget in container.findChildren(QWidget): if isinstance(widget, QLineEdit): widget.clear() elif isinstance(widget, QPlainTextEdit): widget.setPlainText("") elif isinstance(widget, QPushButton): widget.setStyleSheet("") # Restablecer estilos de botones elif widget.objectName().startswith("widget_footer_"): line_edit = widget.findChild(QLineEdit) if line_edit: line_edit.clear() widget.setStyleSheet("") # Restablecer estilos generales def favicon(self): file_path, _ = QFileDialog.getOpenFileName( self, "Select a Favicon (.ico)", "", "Icon files (*.ico)" ) if file_path: self.hugo_memory['favicon'] = file_path self.conclave['button_favicon'] = True button = self.findChild(QPushButton, "button_favicon") if button: button.setStyleSheet("background-color: #4CAF50; color: white;") print(self.hugo_memory) else: self.conclave['button_favicon'] = False button = self.findChild(QPushButton, "button_favicon") if button: button.setStyleSheet("") self.check_conclave() def check_conclave(self): all_completed = True for object_name in self.conclave.keys(): widget = self.findChild(QWidget, object_name) if isinstance(widget, (QLineEdit, QPlainTextEdit)): text = widget.toPlainText() if isinstance(widget, QPlainTextEdit) else widget.text() self.conclave[object_name] = bool(text.strip()) if not self.conclave[object_name]: all_completed = False if all_completed: self.button_next.show() else: self.button_next.hide() pass def go_next(self): for widget in self.findChildren((QLineEdit, QPlainTextEdit)): name = widget.objectName() if not name: parent = widget.parent() if parent and parent.objectName().startswith("widget_footer_"): name = parent.objectName().replace("widget_footer_", "") else: continue text = widget.text().strip() if isinstance(widget, QLineEdit) else widget.toPlainText().strip() self.hugo_memory[name] = text if "line_project" in self.hugo_memory: BasePage.project_selected = self.hugo_memory["line_project"] for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.hide() self.loading_group.show() self.label_loading.setText("Creating website...") self.hugo_memory = {k: v for k, v in self.hugo_memory.items() if not (k.startswith("footer_") and not v.strip())} self.worker = WorkerThread('create_website', BasePage.profile_selected, self.hugo_memory, BasePage.template_selected, BasePage.project_selected) self.worker.result.connect(self.veify_worker) self.worker.start() def veify_worker(self, result): if result: print("Next steps") for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.show() self.loading_group.hide() self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PagePublic"]) else: print("Error: no se pudo crear el sitio.") QMessageBox.warning(self, "Warning", "Please select or import a template first.") for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.show() self.loading_group.hide() def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageTemplates"]) class PagePublic(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.6, QVBoxLayout) for object_name, widget_type, height, text, action in [ ('label_select', QLabel, 60, "Your website has been successfully generated. What would you like to do?", None), ('button_edit_website', QPushButton, 40, "Edit Website", self.edit_website), ('button_view_website', QPushButton, 40, "View Website", self.view_website), ('button_public_website', QPushButton, 40, "Public Website on arweave", self.public_website) ]: widget = widget_type(text) widget.setObjectName(object_name) widget.setFixedHeight(height) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) if widget_type == QLabel: widget.setAlignment(Qt.AlignmentFlag.AlignCenter) if widget_type == QPushButton and action: widget.clicked.connect(action) setattr(self, object_name, widget) self.container_layout.addWidget(widget) self.widget_center.layout().setAlignment(Qt.AlignmentFlag.AlignCenter) self.widget_center.layout().addWidget(self.container) self._worker_key = 'public_arweave' # default fallback def showEvent(self, event): super().showEvent(event) manifest_path = os.path.abspath( f".config_app/profiles/{BasePage.profile_selected}/{BasePage.project_selected}/manifest.json" ) if os.path.isfile(manifest_path): self.label_loading.setText("Refresh Website on arweave...") print("refreesh on arweave") self._worker_key = 'refresh_arweave' else: self.label_loading.setText("Upload Website on arweave...") self._worker_key = 'public_arweave' print("refreesh on arweave") def edit_website(self): p = os.path.abspath(f".config_app/profiles/{BasePage.profile_selected}/{BasePage.project_selected}") if os.path.exists(p): if sys.platform == "win32": os.startfile(p) else: subprocess.run(["open" if sys.platform == "darwin" else "xdg-open", p]) def view_website(self): index_path = f".config_app/profiles/{BasePage.profile_selected}/{BasePage.project_selected}/public/index.html" absolute_path = os.path.abspath(index_path) if os.path.exists(absolute_path): webbrowser.open(f"file://{absolute_path}") def public_website(self): for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.hide() self.loading_group.show() self.worker = WorkerThread( key=self._worker_key, profile_selected=BasePage.profile_selected, project_selected=BasePage.project_selected ) self.worker.result.connect(self.go_next) self.worker.start() def go_next(self, result): for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.show() self.loading_group.hide() if result: manifest_path = os.path.join( ".config_app", "profiles", BasePage.profile_selected, BasePage.project_selected, "manifest.json" ) if os.path.exists(manifest_path): with open(manifest_path, "r", encoding="utf-8") as file: data = json.load(file) created = data.get("created", []) if created and "dataTxId" in created[0]: data_tx_id = created[0]["dataTxId"] webbrowser.open(f"https://arweave.net/{data_tx_id}") self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsites"]) else: QMessageBox.critical(self, "Error", "La publicación en Arweave falló. Revisa tu conexión, claves o drive.") def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageTemplates"]) class PageWebsitesEditor(BasePage):#reformular toda la pagina en aras de que muestre en nombre def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.98, QHBoxLayout) self.button_back.show() self.container_layout.setAlignment(Qt.AlignmentFlag.AlignCenter) self.container_layout.setContentsMargins(0, 0, 0, 0) self.current_index = 0 def showEvent(self, event): super().showEvent(event) while self.container_layout.count(): item = self.container_layout.takeAt(0) widget = item.widget() if widget: widget.deleteLater() profile_path = os.path.join(".config_app", "profiles", BasePage.profile_selected) self.proyectos = {} if os.path.exists(profile_path): for project_name in os.listdir(profile_path): project_path = os.path.join(profile_path, project_name) if os.path.isdir(project_path): themes_path = os.path.join(project_path, "themes") plantilla = None if os.path.exists(themes_path): template_folders = [ f for f in os.listdir(themes_path) if os.path.isdir(os.path.join(themes_path, f)) ] if template_folders: plantilla = template_folders[0] self.proyectos[project_name] = plantilla if not self.proyectos: label = QLabel("No websites yet") label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.container_layout.addWidget(label) else: self.create_structure() def create_structure(self): self.button_next.show() # Botón anterior self.button_before = QPushButton("◀") self.button_before.setFixedSize(40, 40) self.button_before.clicked.connect(self.go_before) self.button_before.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) self.container_layout.addWidget(self.button_before) # Contenedor central con QStackedLayout self.widget_rotary = QLabel() self.widget_rotary.setContentsMargins(0, 0, 0, 0) self.widget_rotary_layout = QStackedLayout(self.widget_rotary) self.widget_rotary_layout.setContentsMargins(0, 0, 0, 0) self.widget_rotary_layout.setSpacing(0) self.widget_rotary.setStyleSheet("background-color: black;") self.widget_rotary.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) self.container_layout.addWidget(self.widget_rotary) self.images = [] # Almacenaremos las imágenes aquí # Cargar imágenes según plantilla for project_name, plantilla in self.proyectos.items(): plantilla_dir = os.path.join(".config_app", "templates", plantilla) image_path = None # Imprimir la ruta donde se está buscando la imagen print(f"Buscando imágenes en: {plantilla_dir}") # Buscar cualquier archivo de imagen en la carpeta de la plantilla if os.path.exists(plantilla_dir): for f in os.listdir(plantilla_dir): if f.lower().endswith((".png", ".jpg", ".jpeg", ".gif")): image_path = os.path.join(plantilla_dir, f) print(f"Imagen encontrada: {image_path}") break # Solo tomamos la primera imagen válida # Crear el QLabel y cargar la imagen image_label = QLabel() image_label.setAlignment(Qt.AlignmentFlag.AlignCenter) # Establecer fondo transparente y expandir el QLabel image_label.setStyleSheet("background-color: transparent;") image_label.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) image_label.setScaledContents(True) # Asegurar que la imagen se escale al tamaño del QLabel if image_path and os.path.exists(image_path): # Establecer la imagen en el QLabel image_label.setPixmap(QPixmap(image_path)) else: image_label.setText(f"No image for '{plantilla}'") # Añadir la imagen al layout y almacenarla para control self.widget_rotary_layout.addWidget(image_label) self.images.append((project_name, image_label)) # Guardar el proyecto y su imagen self.label_balance.setText(project_name) # Botón siguiente self.button_after = QPushButton("▶") self.button_after.setFixedSize(40, 40) self.button_after.clicked.connect(self.go_after) # Usamos go_next para guardar el nombre del proyecto self.button_after.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Fixed) self.container_layout.addWidget(self.button_after) self.current_index = 0 # Empezamos con la primera imagen self.widget_rotary_layout.setCurrentIndex(self.current_index) def go_next(self): # Ahora guardamos BasePage.project_selected como el nombre del proyecto de la imagen que esté viéndose selected_project = self.images[self.current_index][0] # Nombre del proyecto BasePage.project_selected = selected_project print(f"Proyecto seleccionado: {selected_project}") # Guardar también la plantilla seleccionada plantilla = self.proyectos[selected_project] # Obtener la plantilla del proyecto BasePage.template_selected = plantilla # Guardar la plantilla seleccionada print(f"Plantilla seleccionada: {plantilla}") print(BasePage.project_selected) # Opcional: Cambiar a la página de edición, sin modificar la imagen self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageEditWebsite"]) def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageWebsites"]) def go_before(self): if self.widget_rotary_layout.count() > 0: self.current_index = (self.current_index - 1) % self.widget_rotary_layout.count() self.widget_rotary_layout.setCurrentIndex(self.current_index) project_name = self.images[self.current_index][0] plantilla = self.proyectos[project_name] print(f"[◀ Anterior] Proyecto: {project_name} | Plantilla: {plantilla}") self.label_balance.setText(project_name) def go_after(self): if self.widget_rotary_layout.count() > 0: self.current_index = (self.current_index + 1) % self.widget_rotary_layout.count() self.widget_rotary_layout.setCurrentIndex(self.current_index) project_name = self.images[self.current_index][0] plantilla = self.proyectos[project_name] print(f"[▶ Siguiente] Proyecto: {project_name} | Plantilla: {plantilla}") self.label_balance.setText(project_name) class PageEditWebsite(BasePage): def __init__(self, stacked_widget): super().__init__(stacked_widget, 0.98, QHBoxLayout) self.button_back.show() self._last_template_loaded = None self.conclave = {} self.hugo_memory = {} for object_name in [("widget_left"), ("widget_right")]: widget = QWidget() widget.setObjectName(object_name) widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) layout = QVBoxLayout(widget) layout.setContentsMargins(0, 0, 0, 0) setattr(self, object_name, widget) setattr(self, f"{object_name}_layout", layout) self.container_layout.addWidget(widget, 1) for object_name, widget_type, text, action, height in [ ("label_website", QLabel, "General website config", None, 30), ("button_open_website", QPushButton, "Open website in browser", self.open_website, 30), ("button_edit_website", QPushButton, "Edit manually website (public folder)", self.edit, 30), ("button_view_manifest", QPushButton, "View manifest", self.view_manifest, 30), ("label_qr", QLabel, "", None, 80), ("phantom", QLabel, None, None, 30), ("phantom", QLabel, None, None, 80), ("phantom", QLabel, None, None, 30), ("phantom", QLabel, None, None, 30) ]: widget = widget_type() widget.setObjectName(object_name) widget.setFixedHeight(height) if isinstance(widget, (QLabel, QPushButton)): widget.setText(text) if isinstance(widget, QPushButton) and action: widget.clicked.connect(action) self.widget_left_layout.addWidget(widget) self.widget_center.layout().setAlignment(Qt.AlignmentFlag.AlignCenter) self.widget_center.layout().addWidget(self.container) def showEvent(self, event): super().showEvent(event) print(BasePage.project_selected) print(f"Layout is: {BasePage.template_selected}") self.reset_widgets() if self._last_template_loaded == BasePage.template_selected: print("La plantilla ya fue cargada anteriormente, omitiendo carga de widgets.") return for i in reversed(range(self.widget_right_layout.count())): widget = self.widget_right_layout.itemAt(i).widget() if widget: widget.setParent(None) self._last_template_loaded = BasePage.template_selected template_dir = f".config_app/templates/{BasePage.template_selected}/" for file_path in glob.glob(f"{template_dir}*.py"): try: name = os.path.splitext(os.path.basename(file_path))[0] spec = importlib.util.spec_from_file_location(name, file_path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) if hasattr(module, 'add_right_widgets'): print(f"Cargando: {file_path}") module.add_right_widgets(self) except Exception as e: print(f"Error en {file_path} → {e}") template_dir = f".config_app/templates/{BasePage.template_selected}/" # Buscar y cargar archivos .css css_files = glob.glob(os.path.join(template_dir, "*.css")) if css_files: css_path = css_files[0] print(f"Hoja de estilo encontrada: {css_path}") try: with open(css_path, "r", encoding="utf-8") as f: self.setStyleSheet(f.read()) print(f"Estilo aplicado desde: {css_path}") except Exception as e: print(f"Error al cargar el estilo desde {css_path} → {e}") else: print(f"No se encontró ningún archivo .css en: {template_dir}") def reset_widgets(self): for container in [self.widget_left, self.widget_right]: for widget in container.findChildren(QWidget): if isinstance(widget, QLineEdit): widget.clear() elif isinstance(widget, QPlainTextEdit): widget.setPlainText("") elif isinstance(widget, QPushButton): widget.setStyleSheet("") # Restablecer estilos de botones elif widget.objectName().startswith("widget_footer_"): line_edit = widget.findChild(QLineEdit) if line_edit: line_edit.clear() widget.setStyleSheet("") # Restablecer estilos generales def reset_left_widgets(self): for widget in self.widget_left.findChildren(QWidget): if isinstance(widget, QLineEdit): widget.clear() elif isinstance(widget, QPlainTextEdit): widget.setPlainText("") elif isinstance(widget, QPushButton): widget.setStyleSheet("") # Restablecer estilos de botones elif widget.objectName().startswith("widget_footer_"): line_edit = widget.findChild(QLineEdit) if line_edit: line_edit.clear() widget.setStyleSheet("") # Restablecer estilos generales def view_manifest(self): manifest_path = os.path.join( ".config_app", "profiles", BasePage.profile_selected, BasePage.project_selected, "manifest.json" ) if os.path.exists(manifest_path): webbrowser.open(f"file://{os.path.abspath(manifest_path)}") def open_website(self): manifest_path = os.path.join( ".config_app", "profiles", BasePage.profile_selected, BasePage.project_selected, "manifest.json" ) if os.path.exists(manifest_path): with open(manifest_path, "r", encoding="utf-8") as file: data = json.load(file) created = data.get("created", []) if created and "dataTxId" in created[0]: data_tx_id = created[0]["dataTxId"] webbrowser.open(f"https://arweave.net/{data_tx_id}") def edit(self): import os, subprocess, sys manifest_path = os.path.join( ".config_app", "profiles", BasePage.profile_selected, BasePage.project_selected ) if os.path.exists(manifest_path): if sys.platform.startswith('darwin'): subprocess.Popen(['open', manifest_path]) elif os.name == 'nt': os.startfile(manifest_path) elif os.name == 'posix': subprocess.Popen(['xdg-open', manifest_path]) def check_conclave(self): all_completed = True for object_name in self.conclave.keys(): widget = self.findChild(QWidget, object_name) if isinstance(widget, (QLineEdit, QPlainTextEdit)): text = widget.toPlainText() if isinstance(widget, QPlainTextEdit) else widget.text() self.conclave[object_name] = bool(text.strip()) if not self.conclave[object_name]: all_completed = False if all_completed: self.button_next.show() else: self.button_next.hide() pass def go_next(self): for widget in self.findChildren((QLineEdit, QPlainTextEdit)): name = widget.objectName() if not name: parent = widget.parent() if parent and parent.objectName().startswith("widget_footer_"): name = parent.objectName().replace("widget_footer_", "") else: continue text = widget.text().strip() if isinstance(widget, QLineEdit) else widget.toPlainText().strip() self.hugo_memory[name] = text if "line_project" in self.hugo_memory: BasePage.project_selected = self.hugo_memory["line_project"] for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.hide() self.loading_group.show() self.label_loading.setText("edit website...") self.hugo_memory = {k: v for k, v in self.hugo_memory.items() if not (k.startswith("footer_") and not v.strip())} self.worker = WorkerThread('edit_website', BasePage.profile_selected, self.hugo_memory, BasePage.template_selected, BasePage.project_selected) self.worker.result.connect(self.veify_worker) self.worker.start() def veify_worker(self, result): if result: print("Next steps") for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.show() self.loading_group.hide() self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PagePublic"]) else: print("Error: no se pudo crear el sitio.") QMessageBox.warning(self, "Warning", "Please select or import a template first.") for i in reversed(range(self.container_layout.count())): widget = self.container_layout.itemAt(i).widget() if widget: widget.show() self.loading_group.hide() def go_back(self): self.stacked_widget.setCurrentWidget(self.stacked_widget.parent().pages["PageTemplates"]) if __name__ == "__main__": app = QApplication([]) ventana = MiVentana() ventana.show() app.exec()