ArWeb/main.py
2025-05-21 10:32:03 -05:00

1211 lines
51 KiB
Python

# === 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()