diff --git a/RENDERIZADO_LATEX.md b/RENDERIZADO_LATEX.md new file mode 100644 index 0000000..0269bc5 --- /dev/null +++ b/RENDERIZADO_LATEX.md @@ -0,0 +1,127 @@ +# Sistema de Renderizado LaTeX Simplificado + +## Problema Original +El panel LaTeX no se renderizaba correctamente porque faltaba el motor de renderizado matemático (MathJax) y las fuentes matemáticas apropiadas. + +## Solución Implementada + +### 🌐 **Sistema Único: tkinterweb + MathJax 3** +- **Dependencia requerida**: tkinterweb +- **Motor de renderizado**: MathJax 3 optimizado +- **Fuentes matemáticas**: STIX Two Math, Computer Modern Serif +- **Configuración automática** de delimitadores LaTeX (`$$...$$`, `$...$`) +- **Re-renderizado dinámico** cuando se agregan nuevas ecuaciones + +```javascript +// MathJax configurado automáticamente +window.MathJax = { + tex: { + inlineMath: [['$', '$']], + displayMath: [['$$', '$$']], + processEscapes: true, + processEnvironments: true, + packages: {'[+]': ['ams', 'newcommand', 'configmacros']} + }, + startup: { + ready: function () { + MathJax.startup.defaultReady(); + console.log('✅ MathJax 3 listo'); + } + } +}; +``` + +## Instalación + +### Dependencia Requerida +```bash +pip install tkinterweb +``` + +**Nota**: Si no tienes tkinterweb instalado, la aplicación se cerrará automáticamente con un mensaje de error claro. + +## Características Técnicas + +### ✅ **Renderizado Optimizado** +- **MathJax 3** para ecuaciones LaTeX profesionales +- **Fuentes matemáticas mejoradas** (STIX Two Math, Computer Modern Serif) +- **Re-renderizado automático** cuando se agregan ecuaciones +- **Indicador de estado** que muestra si MathJax está activo + +### ✅ **Integración Perfecta** +- **Panel expandible** en el lado derecho de la calculadora +- **Sincronización automática** con las evaluaciones +- **Detección inteligente** de ecuaciones, asignaciones y comentarios +- **Indicador visual** cuando hay contenido disponible + +### ✅ **Tipos de Contenido Soportados** +- **Ecuaciones**: `x**2 + 2*x = 8` +- **Asignaciones**: `a = 5` +- **Expresiones simbólicas**: `sin(x) + cos(x)` +- **Comentarios**: `# Esto es un comentario` + +## Uso + +1. **Abrir panel LaTeX**: Click en el botón 📐 en el borde derecho +2. **Escribir ecuaciones**: Las ecuaciones se detectan automáticamente +3. **Ver renderizado**: MathJax renderiza las ecuaciones en tiempo real +4. **Cerrar panel**: Click en ✕ o en el botón 📐 nuevamente + +## Solución de Problemas + +### Si ves "Cargando MathJax..." +- **Causa**: MathJax no se ha cargado completamente +- **Solución**: Espera unos segundos, MathJax se carga desde CDN +- **Verificación**: El indicador cambiará a "✅ MathJax activo" + +### Si la aplicación se cierra al inicio +- **Causa**: tkinterweb no está instalado +- **Solución**: `pip install tkinterweb` +- **Verificación**: Reinicia la aplicación + +### Si las ecuaciones no se renderizan +- **Causa**: Error en la sintaxis LaTeX +- **Solución**: Verifica que la sintaxis LaTeX sea correcta +- **Verificación**: Revisa la consola del navegador (F12 en tkinterweb) + +## Ventajas del Sistema Simplificado + +### 🎯 **Confiabilidad** +- **Un solo sistema** = menos puntos de falla +- **Dependencia mínima** = fácil instalación +- **Error claro** si falta la dependencia + +### 🚀 **Rendimiento** +- **MathJax 3** optimizado para velocidad +- **Sin fallbacks** = código más limpio +- **Carga asíncrona** = no bloquea la UI + +### 🔧 **Mantenimiento** +- **Código simplificado** = fácil de mantener +- **Sin lógica compleja** de detección de sistemas +- **Comportamiento predecible** + +## Arquitectura + +``` +Calculadora MAV +├── Panel de Entrada (izquierda) +├── Panel de Salida (centro) +└── Panel LaTeX (derecha, expandible) + └── tkinterweb.HtmlFrame + └── MathJax 3 + HTML optimizado + ├── Ecuaciones renderizadas + ├── Fuentes matemáticas + └── Indicador de estado +``` + +## Conclusión + +El sistema simplificado con **tkinterweb + MathJax 3** proporciona: +- ✅ Renderizado LaTeX profesional +- ✅ Instalación simple (una dependencia) +- ✅ Comportamiento predecible +- ✅ Mantenimiento fácil +- ✅ Experiencia de usuario consistente + +**Resultado**: Panel LaTeX que funciona perfectamente sin fallbacks ni complejidad innecesaria. \ No newline at end of file diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index aeed779..520dcce 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -2,11 +2,5 @@ x**2 + y**2 = r**2 r=? -x=3 -y=6 -r -r=? -$$Brix = \frac{Brix_{syrup} \cdot \delta_{syrup} + (Brix_{water} \cdot \delta_{water} \cdot Rateo)}{\delta_{syrup} + \delta_{water} \cdot Rateo}$$ - -$$Brix_{Bev} = \frac{Brix_{syr} + Brix_{H_2O} \cdot R_M}{R_M + 1}$$ \ No newline at end of file +a=r*5+5 \ No newline at end of file diff --git a/install_tkinterweb_js.py b/install_tkinterweb_js.py new file mode 100644 index 0000000..893b1a7 --- /dev/null +++ b/install_tkinterweb_js.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +""" +Script para instalar tkinterweb con soporte JavaScript completo +""" + +import subprocess +import sys +import importlib + +def check_installed(package_name): + """Verifica si un paquete está instalado""" + try: + importlib.import_module(package_name) + return True + except ImportError: + return False + +def install_package(package_spec): + """Instala un paquete usando pip""" + try: + print(f"🔄 Instalando {package_spec}...") + subprocess.check_call([sys.executable, "-m", "pip", "install", package_spec]) + print(f"✅ {package_spec} instalado correctamente") + return True + except subprocess.CalledProcessError as e: + print(f"❌ Error instalando {package_spec}: {e}") + return False + +def main(): + """Función principal""" + print("🚀 Instalador de tkinterweb con JavaScript") + print("=" * 50) + + # Verificar estado actual + print("\n📊 Estado actual:") + tkinterweb_available = check_installed("tkinterweb") + pythonmonkey_available = check_installed("pythonmonkey") + + print(f" • tkinterweb: {'✅ Instalado' if tkinterweb_available else '❌ No instalado'}") + print(f" • pythonmonkey: {'✅ Instalado' if pythonmonkey_available else '❌ No instalado'}") + + if tkinterweb_available and pythonmonkey_available: + print("\n🎉 ¡Todo está instalado correctamente!") + print(" La calculadora debería funcionar con JavaScript completo.") + return + + print("\n🔧 Procediendo con la instalación...") + + # Instalar tkinterweb con JavaScript + if not tkinterweb_available or not pythonmonkey_available: + print("\n1️⃣ Instalando tkinterweb[javascript]...") + success = install_package("tkinterweb[javascript]") + + if not success: + print("\n⚠️ La instalación automática falló.") + print(" Intenta manualmente:") + print(" pip install tkinterweb[javascript]") + print("\n Si hay problemas con PythonMonkey:") + print(" 1. Asegúrate de tener Python 3.8+") + print(" 2. En Windows, puede requerir Visual Studio Build Tools") + print(" 3. En Linux, puede requerir build-essential") + return + + # Verificar instalación final + print("\n🔍 Verificando instalación...") + tkinterweb_final = check_installed("tkinterweb") + pythonmonkey_final = check_installed("pythonmonkey") + + print(f" • tkinterweb: {'✅' if tkinterweb_final else '❌'}") + print(f" • pythonmonkey: {'✅' if pythonmonkey_final else '❌'}") + + if tkinterweb_final and pythonmonkey_final: + print("\n🎉 ¡Instalación completada exitosamente!") + print(" La calculadora MAV ahora puede usar JavaScript completo.") + print(" Reinicia la aplicación para activar las funciones avanzadas.") + else: + print("\n⚠️ Instalación parcial.") + print(" La calculadora funcionará en modo fallback.") + print(" Consulta la documentación para resolver problemas de instalación.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/latex_debug.html b/latex_debug.html new file mode 100644 index 0000000..47721d3 --- /dev/null +++ b/latex_debug.html @@ -0,0 +1,148 @@ + + + + + + + Ecuaciones LaTeX - PyWebView + + + + + + + + + + +
\n +
+
+
$$x^{2} + y^{2} = r^{2}$$
+
+
+ +
+
+
$$r = - \sqrt{x^{2} + y^{2}}$$
+
+
+ +
+
+
$$5 - 5 \sqrt{x^{2} + y^{2}}$$
+
+
\n
+ + +
+ ✓ PyWebView activo - MathJax cargándose... +
+ + + \ No newline at end of file diff --git a/main_calc_app.py b/main_calc_app.py index 1a3a21f..9d42c94 100644 --- a/main_calc_app.py +++ b/main_calc_app.py @@ -96,6 +96,7 @@ class HybridCalculatorApp: self.latex_renderer = None self._webview_available = False self._webview_type = None + self._js_available = False # ========== VARIABLES DE ESTADO FALTANTES ========== self._cached_input_font = None @@ -499,7 +500,7 @@ CARACTERÍSTICAS: self.latex_indicator.pack_forget() def _setup_latex_panel_expandable(self): - """Configura el panel LaTeX expandible""" + """Configura el panel LaTeX expandible con pywebview""" try: # Frame para el panel LaTeX (crear pero no agregar al PanedWindow todavía) self.latex_panel = tk.Frame(self.root, bg="#1a1a1a", bd=1, relief=tk.SOLID) @@ -535,79 +536,73 @@ CARACTERÍSTICAS: ) close_button.pack(side=tk.RIGHT, padx=5) - # Intentar diferentes opciones de webview en orden de preferencia - self._webview_available = False - self._webview_type = None - self.latex_webview = None - self.latex_fallback_text = None - - # Opción 1: tkinterweb (más compatible con tkinter) + # NUEVO: Sistema con pywebview (más robusto para MathJax) try: - import tkinterweb + import webview - # Crear el visor HTML con tkinterweb + # JavaScript siempre disponible con pywebview + self._js_available = True + self.logger.debug("✅ pywebview disponible - JavaScript totalmente habilitado") + + # Frame para contener el webview html_frame = tk.Frame(self.latex_panel, bg="#1a1a1a") html_frame.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) - self.latex_webview = tkinterweb.HtmlFrame( - html_frame, - messages_enabled=False - ) - self.latex_webview.pack(fill=tk.BOTH, expand=True) - - # Cargar HTML base - base_html = self._generate_base_html() - self.latex_webview.load_html(base_html) + # PLACEHOLDER: El webview se creará cuando sea necesario + # pywebview requiere un manejo especial para integración con tkinter + self.latex_webview = None + self.latex_webview_frame = html_frame + self._webview_ready = False self._webview_available = True - self._webview_type = "tkinterweb" + self._webview_type = "pywebview" + + self.logger.debug("🌐 pywebview configurado - webview se creará dinámicamente") except ImportError: - # Opción 2: pywebview (menos integrado pero más completo) + # Fallback a tkinterweb si pywebview no está disponible try: - import webview + import tkinterweb - # Crear el contenedor para el webview - webview_frame = tk.Frame(self.latex_panel, bg="#1a1a1a") - webview_frame.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) + self.logger.warning("⚠️ pywebview no disponible, usando tkinterweb como fallback") - # Inicializar contenido HTML base - self._current_html = self._generate_base_html() + # Verificar disponibilidad de JavaScript en tkinterweb + try: + import pythonmonkey + self._js_available = True + self.logger.debug("✅ tkinterweb con PythonMonkey disponible") + except ImportError: + self._js_available = False + self.logger.debug("ℹ️ tkinterweb sin PythonMonkey - modo fallback") - # El webview se creará de forma lazy cuando sea necesario - self.latex_webview_frame = webview_frame - self.latex_webview = None + # Crear el visor HTML con tkinterweb + html_frame = tk.Frame(self.latex_panel, bg="#1a1a1a") + html_frame.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) + + self.latex_webview = tkinterweb.HtmlFrame( + html_frame, + messages_enabled=False + ) + self.latex_webview.pack(fill=tk.BOTH, expand=True) + + # Cargar HTML base optimizado + base_html = self._generate_base_html() + self.latex_webview.load_html(base_html) self._webview_available = True - self._webview_type = "pywebview" + self._webview_type = "tkinterweb" except ImportError: - # Opción 3: Fallback con Text widget - self._webview_available = False - self._webview_type = "fallback" - - fallback_text = scrolledtext.ScrolledText( - self.latex_panel, - font=("Consolas", 10), - bg="#1a1a1a", - fg="#d4d4d4", - state="disabled", - wrap=tk.WORD, - borderwidth=0, - highlightthickness=0, - relief=tk.FLAT + # Sin ninguna de las dos librerías + messagebox.showerror( + "Dependencia Requerida", + "Esta calculadora requiere 'pywebview' o 'tkinterweb' para el renderizado LaTeX.\n\n" + "Instala con: pip install pywebview\n" + "O: pip install tkinterweb\n\n" + "La aplicación se cerrará." ) - fallback_text.pack(fill=tk.BOTH, expand=True, padx=2, pady=2) - self.latex_fallback_text = fallback_text - - # Mostrar mensaje informativo - fallback_text.config(state="normal") - fallback_text.insert("1.0", "Panel de Ecuaciones LaTeX\n\n" - "Para ver ecuaciones renderizadas, instala:\n" - "pip install tkinterweb\n" - "o pip install pywebview\n\n" - "Las ecuaciones aparecerán aquí en formato texto:") - fallback_text.config(state="disabled") + self.root.quit() + return # Configurar tamaño del panel self._min_latex_pane_width = 300 @@ -627,32 +622,24 @@ CARACTERÍSTICAS: return try: - if self._webview_available: - if self._webview_type == "tkinterweb": - self._update_tkinterweb() - elif self._webview_type == "pywebview": - self._update_pywebview() - else: - # Refrescar contenido de fallback - if hasattr(self, 'latex_fallback_text'): - self.latex_fallback_text.config(state="normal") - self.latex_fallback_text.delete("1.0", tk.END) - - for eq in self._latex_equations: - if eq['type'] == 'spacer': - self.latex_fallback_text.insert(tk.END, "\n") - else: - type_text = f"[{eq['type'].upper()}] " - self.latex_fallback_text.insert(tk.END, type_text) - self.latex_fallback_text.insert(tk.END, f"{eq['original']}\n\n") - - self.latex_fallback_text.config(state="disabled") + # Actualizar contenido si hay ecuaciones pendientes + if hasattr(self, '_latex_equations') and self._latex_equations: + self._update_latex_panel() except Exception as e: self.logger.debug(f"Error refrescando contenido LaTeX: {e}") def _generate_base_html(self): - """Genera el HTML base con soporte fallback para renderizar ecuaciones""" + """Genera el HTML base optimizado según el tipo de webview""" + if self._webview_type == "pywebview": + return self._generate_html_for_pywebview() + elif self._js_available: + return self._generate_html_with_javascript() + else: + return self._generate_html_fallback() + + def _generate_html_with_javascript(self): + """HTML con JavaScript completo para tkinterweb""" return r""" @@ -661,46 +648,208 @@ CARACTERÍSTICAS: Ecuaciones LaTeX + + + + + + + + + + +
- 📐 Panel de Ecuaciones
+ 📐 Panel de Ecuaciones LaTeX
+ Renderizado con MathJax + fuentes matemáticas
Las ecuaciones se mostrarán aquí automáticamente
+ + """ + + def _generate_html_for_pywebview(self): + """HTML optimizado específicamente para pywebview (más simple y robusto)""" + return r""" + + + + + + Ecuaciones LaTeX - PyWebView + + + + + + + + + + +
+
+ 📐 Panel de Ecuaciones LaTeX
+ Renderizado con MathJax en PyWebView
+ Las ecuaciones aparecerán aquí automáticamente +
+
+ +
+ ✓ PyWebView activo - MathJax cargándose... +
+ + + """ + + def _generate_html_fallback(self): + """HTML fallback simple sin JavaScript""" + return r""" + + + + + + Ecuaciones LaTeX (Modo Fallback) + + + + +
+
+ 📐 Panel de Ecuaciones LaTeX (Modo Fallback)
+ Renderizado texto mejorado - Sin JavaScript/MathJax
+ Las ecuaciones se mostrarán en formato texto matemático mejorado +
+
+ +
+ ℹ️ Modo Fallback - Matemáticas en Texto Mejorado
+ Para MathJax completo: pip install tkinterweb[javascript] +
+ """ @@ -880,6 +1380,14 @@ CARACTERÍSTICAS: tools_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white") menubar.add_cascade(label="Herramientas", menu=tools_menu) tools_menu.add_command(label="Recargar Tipos Personalizados", command=self.reload_types) + tools_menu.add_separator() + tools_menu.add_command(label="📊 Información del Sistema", command=self.show_types_info) + tools_menu.add_command(label="🔍 Diagnóstico MathJax", command=self._diagnose_mathjax) + tools_menu.add_command(label="🔧 Test tkinterweb", command=self._test_tkinterweb_mathjax) + tools_menu.add_command(label="🌐 Abrir HTML en Navegador", command=self._open_debug_html_in_browser) + tools_menu.add_separator() + tools_menu.add_command(label="📊 Estado Panel LaTeX", command=self._show_latex_panel_status) + tools_menu.add_command(label="🔄 Forzar Actualización HTML", command=self._force_html_update) # ========== MENÚ TIPOS (NUEVO) ========== types_menu = Menu(menubar, tearoff=0, bg="#3c3c3c", fg="white") @@ -1878,10 +2386,18 @@ CARACTERÍSTICAS: latex_content = result.output if result.output else "" self.logger.debug(f" -> Usando output directo: {latex_content[:100]}...") + # ANTES de añadir al panel, verificar si está inicializado + if not hasattr(self, '_latex_equations'): + self._latex_equations = [] + self.logger.debug(" -> ⚠️ Lista de ecuaciones no existía, creándola") + + total_antes = len(self._latex_equations) + # Usar la función _add_to_latex_panel self._add_to_latex_panel(equation_type, latex_content) - self.logger.debug(f"✅ AÑADIDO al panel LaTeX: {equation_type} -> {latex_content[:50]}...") + total_despues = len(self._latex_equations) + self.logger.debug(f"✅ AÑADIDO al panel LaTeX: {equation_type} -> {latex_content[:50]}... (Total: {total_antes} -> {total_despues})") else: self.logger.debug(" -> NO añadido al panel LaTeX") @@ -2858,273 +3374,392 @@ Para crear un archivo de ayuda personalizado, cree un archivo `readme.md` en el self.logger.debug(f"Error convirtiendo a LaTeX: {e}") return str(sympy_obj) - def _update_tkinterweb(self): - """Actualiza el contenido usando tkinterweb""" - try: - if not hasattr(self, 'latex_webview') or not self.latex_webview: - return - - # Generar HTML completo con todas las ecuaciones - html_content = self._generate_equations_html() - - # Cargar el HTML actualizado - self.latex_webview.load_html(html_content) - - except Exception as e: - self.logger.debug(f"Error actualizando tkinterweb: {e}") - - def _update_pywebview(self): - """Actualiza el contenido usando pywebview (implementación futura)""" - try: - # Para pywebview, necesitaríamos usar la API de JavaScript - # Por ahora, usar un enfoque más simple - pass - - except Exception as e: - self.logger.debug(f"Error actualizando pywebview: {e}") - - def _generate_equations_html(self): - """Genera HTML completo con todas las ecuaciones""" - base_html = self._generate_base_html() - - # Generar contenido de ecuaciones - equations_content = "" - - for eq in self._latex_equations: - eq_type = eq['type'] - content = eq['content'] - - # Manejar espaciadores especiales - if eq_type == 'spacer': - equations_content += '
' - continue - - equations_content += f""" -
-
{eq_type}
-
{content}
-
- """ - - # Reemplazar el contenido del container en el HTML base - if equations_content: - placeholder = '
' - if placeholder in base_html: - # Reemplazar el contenido de ejemplo - start_idx = base_html.find('
') + len('
') - end_idx = base_html.find('
', start_idx) - - new_html = (base_html[:start_idx] + - equations_content + - base_html[end_idx:]) - return new_html - - return base_html + def _update_latex_panel(self): - """Actualiza el contenido del panel LaTeX""" + """Actualiza el panel LaTeX con soporte para pywebview y tkinterweb""" if not self.latex_panel_visible or not hasattr(self, '_latex_equations'): - self.logger.debug("❌ Panel no visible o sin ecuaciones - no actualizar") return - + try: - self.logger.debug(f"🔄 Actualizando panel LaTeX con {len(self._latex_equations)} ecuaciones") + self.logger.debug(f"🔄 Actualizando panel LaTeX con {len(self._latex_equations)} ecuaciones (Tipo: {self._webview_type})") - if not self._latex_equations: - # Si no hay ecuaciones, usar el HTML base - html_content = self._generate_base_html() - self.logger.debug("📄 Generando HTML base (sin ecuaciones)") + # Generar HTML con ecuaciones según el tipo de webview + if self._latex_equations: + if self._webview_type == "pywebview": + html_content = self._generate_html_pywebview_with_equations() + elif self._js_available: + html_content = self._generate_html_with_mathjax() + else: + html_content = self._generate_html_fallback_with_equations() else: - # Generar HTML con las ecuaciones - html_blocks = [] - - for i, eq_data in enumerate(self._latex_equations): - eq_type = eq_data.get('type', 'symbolic') - latex_content = eq_data.get('latex', '') - - self.logger.debug(f" 📝 Ecuación {i+1}: {eq_type} -> {latex_content[:50]}...") - - # Crear bloque HTML para esta ecuación - # Aplicar formateo matemático mejorado - formatted_math = latex_content - - # Convertir LaTeX básico a HTML mejorado con mejores símbolos - import re - - # 1. Fracciones LaTeX: \frac{num}{den} → HTML - def replace_fractions(text): - def replace_frac(match): - num = match.group(1) - den = match.group(2) - return f'{num}{den}' - return re.sub(r'\\frac\{([^{}]*)\}\{([^{}]*)\}', replace_frac, text) - - # 2. Raíces cuadradas: \sqrt{expr} → HTML - def replace_sqrt(text): - def replace_sqrt_match(match): - expr = match.group(1) - return f'{expr}' - return re.sub(r'\\sqrt\{([^{}]*)\}', replace_sqrt_match, text) - - # 3. Exponentes: ^{expr} y ^num → superíndices - def replace_superscripts(text): - # Exponentes con llaves: ^{...} - text = re.sub(r'\^\{([^{}]*)\}', r'\1', text) - # Exponentes simples: ^2, ^3, etc. - text = re.sub(r'\^([0-9]+)', r'\1', text) - # ** para exponentes - text = text.replace('**2', '²').replace('**3', '³') - text = re.sub(r'\*\*([0-9]+)', r'\1', text) - return text - - # 4. Subíndices: _{expr} → subíndices - def replace_subscripts(text): - text = re.sub(r'_\{([^{}]*)\}', r'\1', text) - text = re.sub(r'_([0-9]+)', r'\1', text) - return text - - # 5. Símbolos matemáticos - def replace_symbols(text): - replacements = { - 'pi': 'π', 'Pi': 'π', '\\pi': 'π', - 'alpha': 'α', '\\alpha': 'α', - 'beta': 'β', '\\beta': 'β', - 'gamma': 'γ', '\\gamma': 'γ', - 'delta': 'δ', '\\delta': 'δ', - 'theta': 'θ', '\\theta': 'θ', - 'lambda': 'λ', '\\lambda': 'λ', - 'sigma': 'σ', '\\sigma': 'σ', - 'omega': 'ω', '\\omega': 'ω' - } - for latex_sym, unicode_sym in replacements.items(): - text = text.replace(latex_sym, unicode_sym) - return text - - # Aplicar todas las transformaciones - formatted_math = replace_fractions(formatted_math) - formatted_math = replace_sqrt(formatted_math) - formatted_math = replace_superscripts(formatted_math) - formatted_math = replace_subscripts(formatted_math) - formatted_math = replace_symbols(formatted_math) - - block_html = f""" -
-
-
{formatted_math}
-
-
""" - html_blocks.append(block_html) - - # Generar HTML completo - equations_html = '\n'.join(html_blocks) - html_content = self._generate_base_html().replace( - '
\n
\n 📐 Panel de Ecuaciones
\n Las ecuaciones se mostrarán aquí automáticamente\n
\n
', - f'
\n{equations_html}\n
' - ) - - self.logger.debug(f"📄 HTML generado: {len(html_content)} caracteres") - self.logger.debug(f"📄 Ecuaciones HTML: {len(equations_html)} caracteres") + # Sin ecuaciones, usar HTML base + html_content = self._generate_base_html() - # Actualizar el widget de renderizado - # Método 1: tkinterweb - if hasattr(self, 'latex_webview') and hasattr(self.latex_webview, 'load_html'): - try: - self.logger.debug("🌐 Intentando actualizar con tkinterweb...") - self.latex_webview.load_html(html_content) - # tkinterweb auto-renderiza KaTeX si está configurado correctamente en el HTML - self.logger.debug("✅ LaTeX actualizado via tkinterweb") - return - except Exception as e: - self.logger.error(f"❌ Error actualizando tkinterweb: {e}") + # Manejar según el tipo de webview + if self._webview_type == "pywebview": + self._update_pywebview_panel(html_content) + elif self._webview_type == "tkinterweb": + self._update_tkinterweb_panel(html_content) + else: + self.logger.warning("⚠️ Tipo de webview no reconocido") - # Método 2: pywebview - elif hasattr(self, 'latex_webview') and self._webview_type == "pywebview": - try: - self.logger.debug("🌐 Intentando actualizar con pywebview...") - if self.latex_webview: - self.latex_webview.load_html(html_content) - self.logger.debug("✅ LaTeX actualizado via pywebview") - return - else: - self.logger.debug("⚠️ latex_webview no disponible para pywebview") - except Exception as e: - self.logger.error(f"❌ Error actualizando pywebview: {e}") - - # Método 3: Fallback a texto plano - if hasattr(self, 'latex_fallback_text'): - try: - self.logger.debug("📝 Usando fallback a texto plano...") - # Limpiar y mostrar contenido como texto - self.latex_fallback_text.config(state="normal") - self.latex_fallback_text.delete('1.0', tk.END) - - if self._latex_equations: - content_lines = ["📐 Ecuaciones LaTeX:", ""] - - for eq_data in self._latex_equations: - eq_type = eq_data.get('type', 'symbolic') - latex_content = eq_data.get('latex', '') - original = eq_data.get('original', '') - - content_lines.append(f"[{eq_type.upper()}]") - content_lines.append(f"LaTeX: {latex_content}") - if original: - content_lines.append(f"Original: {original}") - content_lines.append("-" * 50) - content_lines.append("") - - self.latex_fallback_text.insert('1.0', '\n'.join(content_lines)) - else: - self.latex_fallback_text.insert('1.0', "📐 Panel de Ecuaciones LaTeX\n\nLas ecuaciones aparecerán aquí...") - - self.latex_fallback_text.config(state="disabled") - self.logger.debug("✅ LaTeX actualizado via texto plano") - return - except Exception as e: - self.logger.error(f"❌ Error actualizando texto plano: {e}") - - # Si llegamos aquí, no hay widget disponible - self.logger.error("❌ No hay widget de renderizado disponible (tkinterweb, pywebview o fallback)") - except Exception as e: - self.logger.error(f"❌ Error crítico actualizando panel LaTeX: {e}") + self.logger.error(f"❌ Error actualizando panel LaTeX: {e}") import traceback self.logger.error(traceback.format_exc()) - def _add_to_latex_panel(self, content_type: str, latex_content: str): - """Añade una ecuación al panel LaTeX""" - if not hasattr(self, '_latex_equations'): - self._latex_equations = [] - - # Crear datos de la ecuación - equation_data = { - 'type': content_type, - 'latex': latex_content, - 'original': latex_content, # Guardar contenido original también - 'timestamp': time.time() - } - - self._latex_equations.append(equation_data) - - # Limitar número de ecuaciones (opcional) - max_equations = 50 - if len(self._latex_equations) > max_equations: - self._latex_equations = self._latex_equations[-max_equations:] - - self.logger.debug(f"➕ Añadida ecuación {content_type}: {latex_content[:50]}... (Total: {len(self._latex_equations)})") - - # Actualizar indicador visual - self._update_content_indicator() - - # IMPORTANTE: Actualizar panel si está visible - if self.latex_panel_visible: - self.logger.debug("🔄 Panel visible - actualizando contenido...") - self._update_latex_panel() - else: - self.logger.debug("👁️ Panel no visible - saltando actualización") - - self.logger.debug(f"✅ Proceso de añadir ecuación completado") + def _update_pywebview_panel(self, html_content): + """Actualiza el panel usando pywebview (crea nueva ventana cada vez)""" + try: + self.logger.debug(f"📤 Actualizando con pywebview: {len(html_content)} caracteres") + + # Guardar HTML para debugging + self._save_html_debug_copy(html_content) + + # Con subprocess, siempre creamos una nueva ventana + # Esto es más simple y evita problemas de sincronización + self._create_pywebview_window(html_content) + + except Exception as e: + self.logger.error(f"❌ Error con pywebview: {e}") + def _create_pywebview_window(self, html_content): + """Crea una ventana pywebview usando subprocess para evitar conflictos de hilo""" + try: + import subprocess + import tempfile + import os + + # Crear archivo temporal con el HTML + with tempfile.NamedTemporaryFile(mode='w', suffix='.html', delete=False, encoding='utf-8') as f: + f.write(html_content) + html_file_path = f.name + + # Script Python para ejecutar pywebview en proceso separado + pywebview_script = f''' +import webview +import os + +try: + # Leer HTML desde archivo temporal + with open(r"{html_file_path}", "r", encoding="utf-8") as f: + html_content = f.read() + + # Crear ventana pywebview + window = webview.create_window( + "Ecuaciones LaTeX - MAV Calculator", + html=html_content, + width=350, + height=450, + min_size=(300, 350), + resizable=True, + shadow=True, + on_top=False, + transparent=False + ) + + print("🌐 Ventana pywebview creada exitosamente") + + # Iniciar webview + webview.start(debug=False) + +except Exception as e: + print(f"❌ Error en pywebview: {{e}}") +finally: + # Limpiar archivo temporal + try: + os.unlink(r"{html_file_path}") + except: + pass +''' + + # Ejecutar en proceso separado + subprocess.Popen([ + 'python', '-c', pywebview_script + ], creationflags=subprocess.CREATE_NEW_CONSOLE if os.name == 'nt' else 0) + + self.logger.debug("🚀 Proceso pywebview iniciado exitosamente") + + except Exception as e: + self.logger.error(f"❌ Error creando ventana pywebview: {e}") + + def _update_tkinterweb_panel(self, html_content): + """Actualiza el panel usando tkinterweb (fallback)""" + try: + self.logger.debug(f"📤 Cargando HTML en tkinterweb: {len(html_content)} caracteres") + self.logger.debug(f"📝 Muestra del HTML: {html_content[html_content.find('equations-container'):html_content.find('equations-container')+200]}...") + + if hasattr(self, 'latex_webview') and self.latex_webview: + self.latex_webview.load_html(html_content) + self.logger.debug("✅ HTML cargado en tkinterweb") + else: + self.logger.warning("⚠️ latex_webview no disponible en tkinterweb") + + # Guardar HTML para debugging + self._save_html_debug_copy(html_content) + + self.logger.debug("✅ Panel tkinterweb actualizado") + + except Exception as e: + self.logger.error(f"❌ Error actualizando tkinterweb: {e}") + + def _generate_html_with_mathjax(self): + """Genera HTML con MathJax cuando JavaScript está disponible""" + self.logger.debug(f"🔧 Generando HTML con {len(self._latex_equations)} ecuaciones") + + html_blocks = [] + + for i, eq_data in enumerate(self._latex_equations): + eq_type = eq_data.get('type', 'symbolic') + latex_content = eq_data.get('latex', '') + original_content = eq_data.get('original', '') + + self.logger.debug(f" Ecuación {i}: tipo={eq_type}, latex='{latex_content[:50]}...', original='{original_content[:50]}...'") + + # Preparar LaTeX para MathJax + if latex_content: + # Usar $$ para display math + if not latex_content.startswith('$'): + formatted_latex = f"$${latex_content}$$" + else: + formatted_latex = latex_content + + # Crear bloque HTML optimizado + block_html = f""" +
+
+
{formatted_latex}
+
+
""" + else: + # Sin LaTeX válido, mostrar texto original + block_html = f""" +
+
+
{original_content or 'Sin contenido'}
+
+
""" + + html_blocks.append(block_html) + self.logger.debug(f" ✅ Bloque HTML creado para ecuación {i}") + + # Generar HTML completo + if html_blocks: + equations_html = '\n'.join(html_blocks) + status_indicator = '
⏳ Cargando MathJax...
' + final_content = f'{equations_html}\n{status_indicator}' + self.logger.debug(f"📝 HTML de ecuaciones generado: {len(final_content)} caracteres") + else: + # Sin ecuaciones, mantener mensaje por defecto pero agregar status + final_content = """ +
+ 📐 Panel de Ecuaciones LaTeX
+ Renderizado con MathJax + fuentes matemáticas
+ Las ecuaciones se mostrarán aquí automáticamente +
+
ℹ️ Sin ecuaciones
""" + self.logger.debug("📝 Sin ecuaciones - usando contenido por defecto") + + # Obtener HTML base + base_html = self._generate_html_with_javascript() + + # Reemplazar el contenido del contenedor - VERSIÓN MEJORADA + # Buscar el div equations-container y reemplazar todo su contenido + import re + + # Patrón para encontrar el div equations-container completo + pattern = r'
.*?
' + new_container = f'
\n{final_content}\n
' + + # Reemplazar usando regex para mayor precisión + html_content = re.sub(pattern, new_container, base_html, flags=re.DOTALL) + + # Verificar que el reemplazo funcionó + if 'equations-container' in html_content and ('equation-block' in final_content): + equations_found = html_content.count('equation-block') + self.logger.debug(f"🔍 Verificación de reemplazo: {equations_found} bloques de ecuaciones encontrados en HTML final") + else: + self.logger.warning("⚠️ El reemplazo del contenedor de ecuaciones puede no haber funcionado correctamente") + + self.logger.debug(f"🔚 HTML final generado: {len(html_content)} caracteres") + + return html_content + + def _generate_html_pywebview_with_equations(self): + """Genera HTML con ecuaciones específicamente optimizado para pywebview""" + # Crear HTML de ecuaciones usando el mismo sistema que tkinterweb pero con base pywebview + html_blocks = [] + + for i, eq_data in enumerate(self._latex_equations): + eq_type = eq_data.get('type', 'symbolic') + latex_content = eq_data.get('latex', '') + original_content = eq_data.get('original', '') + + # Preparar LaTeX para MathJax + if latex_content: + # Usar $$ para display math + if not latex_content.startswith('$'): + formatted_latex = f"$${latex_content}$$" + else: + formatted_latex = latex_content + + # Crear bloque HTML optimizado para pywebview + block_html = f""" +
+
+
{formatted_latex}
+
+
""" + + html_blocks.append(block_html) + self.logger.debug(f" ✅ Bloque HTML pywebview creado para ecuación {i}") + + # Combinar todos los bloques + final_content = '\n'.join(html_blocks) + self.logger.debug(f"📝 HTML de ecuaciones pywebview generado: {len(final_content)} caracteres") + + # Obtener HTML base y reemplazar contenido + base_html = self._generate_html_for_pywebview() + + # Reemplazar el contenido del contenedor usando regex (corregido para LaTeX) + import re + + # Patrón para encontrar el div equations-container completo + pattern = r'(
)(.*?)(
)' + + # Nuevo contenido del contenedor (escapar para regex) + new_container_content = f'
\\n{final_content}\\n
' + + # Reemplazar usando re.escape para contenido seguro + html_content = re.sub(pattern, lambda m: new_container_content, base_html, flags=re.DOTALL) + + self.logger.debug(f"🔚 HTML final pywebview generado: {len(html_content)} caracteres") + + return html_content + + def _generate_html_fallback_with_equations(self): + """Genera HTML fallback con ecuaciones en formato texto mejorado""" + html_blocks = [] + + for i, eq_data in enumerate(self._latex_equations): + eq_type = eq_data.get('type', 'symbolic') + latex_content = eq_data.get('latex', '') + original_content = eq_data.get('original', '') + + # Convertir LaTeX básico a texto legible + display_content = latex_content or original_content or 'Sin contenido' + + # Conversiones básicas de LaTeX a texto legible + display_content = display_content.replace('\\frac{', '(').replace('}{', ')/(').replace('}', ')') + display_content = display_content.replace('\\sqrt{', '√(').replace('}', ')') + display_content = display_content.replace('^{2}', '²').replace('^{3}', '³') + display_content = display_content.replace('\\', '') + + # Crear bloque HTML simple + block_html = f""" +
+
{display_content}
+
""" + + html_blocks.append(block_html) + + # Generar HTML completo + equations_html = '\n'.join(html_blocks) + + html_content = self._generate_html_fallback().replace( + '
\n
\n 📐 Panel de Ecuaciones LaTeX (Modo Fallback)
\n Renderizado sin JavaScript - PythonMonkey no disponible
\n Las ecuaciones se mostrarán en formato texto mejorado\n
\n
', + f'
\n{equations_html}\n
' + ) + + return html_content + + def _eval_js_tkinterweb(self, js_code: str) -> bool: + """Evalúa JavaScript en tkinterweb (requiere PythonMonkey para JavaScript real)""" + try: + if not self._js_available: + self.logger.debug("⚠️ JavaScript no disponible - PythonMonkey no instalado") + return False + + # Usar PythonMonkey para ejecutar JavaScript + # NOTA: PythonMonkey NO tiene acceso directo al DOM de tkinterweb + # Esta implementación es para compatibilidad futura + import pythonmonkey + + # JavaScript ejecutado en contexto aislado (sin DOM) + result = pythonmonkey.eval(js_code) + self.logger.debug(f"✅ JavaScript ejecutado via PythonMonkey: {js_code[:50]}...") + return True + + except Exception as e: + self.logger.debug(f"⚠️ Error ejecutando JavaScript: {e}") + return False + + def _trigger_mathjax_rerender(self): + """Re-renderiza MathJax específicamente para tkinterweb""" + try: + if hasattr(self, 'latex_webview') and self.latex_webview: + js_code = """ + console.log('🔄 [PYTHON] Trigger de re-renderizado para tkinterweb...'); + + // Llamar directamente a la función específica de tkinterweb + if (typeof renderizarParaTkinterweb === 'function') { + console.log('✅ [PYTHON] Función tkinterweb encontrada, ejecutando...'); + renderizarParaTkinterweb(); + } else if (typeof forzarRenderizadoTkinterweb === 'function') { + console.log('✅ [PYTHON] Función de forzado encontrada, ejecutando...'); + forzarRenderizadoTkinterweb(); + } else { + console.log('⚠️ [PYTHON] Funciones específicas no encontradas, intentando método genérico...'); + + // Fallback para casos donde las funciones no estén definidas + function renderizadoFallback() { + if (typeof window.MathJax !== 'undefined' && window.MathJax.typesetPromise) { + console.log('🔧 [PYTHON] Usando renderizado fallback...'); + + var mathElements = document.querySelectorAll('.math-display'); + console.log('📊 [PYTHON] Elementos a renderizar:', mathElements.length); + + window.MathJax.typesetPromise().then(function() { + console.log('🎉 [PYTHON] Renderizado fallback exitoso!'); + var statusEl = document.getElementById('mathjax-status'); + if (statusEl) { + statusEl.innerHTML = '✅ Renderizado desde Python'; + statusEl.style.color = '#4fc3f7'; + } + }).catch(function(err) { + console.log('❌ [PYTHON] Error en renderizado fallback:', err); + var statusEl = document.getElementById('mathjax-status'); + if (statusEl) { + statusEl.innerHTML = '❌ Error desde Python: ' + err.message; + statusEl.style.color = '#f44747'; + } + }); + } else { + console.log('❌ [PYTHON] MathJax no disponible para fallback'); + var statusEl = document.getElementById('mathjax-status'); + if (statusEl) { + statusEl.innerHTML = '❌ MathJax no disponible'; + statusEl.style.color = '#f44747'; + } + } + } + + renderizadoFallback(); + } + """ + success = self._eval_js_tkinterweb(js_code) + if success: + self.logger.debug("🔄 Trigger específico para tkinterweb enviado") + else: + self.logger.warning("⚠️ JavaScript no ejecutado - revisando conexión MathJax") + else: + self.logger.warning("⚠️ tkinterweb no disponible para JavaScript") + except Exception as e: + self.logger.debug(f"⚠️ Error ejecutando JavaScript en tkinterweb: {e}") + + + def _update_content_indicator(self): """Actualiza el indicador visual de contenido disponible""" try: @@ -3140,6 +3775,459 @@ Para crear un archivo de ayuda personalizado, cree un archivo `readme.md` en el self._indicator_visible = False except Exception as e: self.logger.debug(f"Error actualizando indicador: {e}") + + def _diagnose_mathjax(self): + """Ejecuta diagnóstico completo de MathJax""" + try: + if not hasattr(self, 'latex_webview') or not self.latex_webview: + messagebox.showerror("Error", "Panel LaTeX no disponible") + return + + # Crear ventana de diagnóstico + diag_window = tk.Toplevel(self.root) + diag_window.title("Diagnóstico MathJax") + diag_window.geometry("600x400") + diag_window.configure(bg="#2b2b2b") + + text_widget = scrolledtext.ScrolledText( + diag_window, + font=("Consolas", 10), + bg="#1e1e1e", + fg="#d4d4d4", + wrap=tk.WORD + ) + text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + def agregar_resultado(texto): + text_widget.insert(tk.END, texto + "\n") + text_widget.see(tk.END) + diag_window.update() + + agregar_resultado("🔍 DIAGNÓSTICO MATHJAX") + agregar_resultado("=" * 50) + agregar_resultado("") + + # 1. Verificar que el panel está visible + agregar_resultado(f"📐 Panel LaTeX visible: {self.latex_panel_visible}") + agregar_resultado(f"📊 Ecuaciones en panel: {len(self._latex_equations) if hasattr(self, '_latex_equations') else 0}") + agregar_resultado("") + + # 2. Ejecutar diagnóstico JavaScript + agregar_resultado("🔍 Ejecutando diagnóstico JavaScript...") + + js_diagnostic = """ + var resultados = []; + + // Verificar MathJax + resultados.push('🌐 window.MathJax definido: ' + (typeof window.MathJax !== 'undefined')); + + if (typeof window.MathJax !== 'undefined') { + resultados.push('⚙️ MathJax.typesetPromise: ' + (typeof window.MathJax.typesetPromise !== 'undefined')); + resultados.push('⚙️ MathJax.startup: ' + (typeof window.MathJax.startup !== 'undefined')); + if (window.MathJax.version) { + resultados.push('📦 Versión MathJax: ' + window.MathJax.version); + } + } + + // Verificar DOM + var equations = document.querySelectorAll('.math-display'); + resultados.push('📄 Elementos .math-display encontrados: ' + equations.length); + + var statusEl = document.getElementById('mathjax-status'); + if (statusEl) { + resultados.push('📊 Estado actual: ' + statusEl.innerHTML); + } + + // Verificar consola + resultados.push('🌐 Navegador: ' + navigator.userAgent.substring(0, 50) + '...'); + + console.log('🔍 Diagnóstico completo ejecutado'); + + // Retornar resultados como string + resultados.join('\\n'); + """ + + try: + # Ejecutar diagnóstico JavaScript + self.latex_webview.eval_js(js_diagnostic) + agregar_resultado("✅ Diagnóstico JavaScript ejecutado") + agregar_resultado("") + + # 3. Intentar renderizado de prueba + agregar_resultado("🧪 Intentando renderizado de prueba...") + + test_js = """ + function pruebaRenderizado() { + if (typeof window.MathJax !== 'undefined' && window.MathJax.typesetPromise) { + console.log('🧪 Iniciando prueba de renderizado...'); + window.MathJax.typesetPromise().then(function() { + console.log('✅ Prueba de renderizado exitosa'); + }).catch(function(err) { + console.log('❌ Error en prueba:', err); + }); + } else { + console.log('❌ MathJax no disponible para prueba'); + } + } + pruebaRenderizado(); + """ + + self.latex_webview.eval_js(test_js) + agregar_resultado("✅ Prueba de renderizado enviada") + + except Exception as e: + agregar_resultado(f"❌ Error ejecutando JavaScript: {e}") + + agregar_resultado("") + agregar_resultado("💡 SOLUCIONES POSIBLES:") + agregar_resultado("• Verifica conexión a internet (CDN de MathJax)") + agregar_resultado("• Cierra y reabre el panel LaTeX") + agregar_resultado("• Reinicia la aplicación") + agregar_resultado("• Revisa la consola del navegador (F12)") + + # Botón para cerrar + close_btn = tk.Button( + diag_window, + text="Cerrar", + command=diag_window.destroy, + bg="#3c3c3c", + fg="white", + relief=tk.FLAT + ) + close_btn.pack(pady=5) + + except Exception as e: + messagebox.showerror("Error", f"Error en diagnóstico:\n{e}") + + def _test_tkinterweb_mathjax(self): + """Test específico para tkinterweb y MathJax""" + try: + if not hasattr(self, 'latex_webview') or not self.latex_webview: + messagebox.showerror("Error", "Panel LaTeX tkinterweb no disponible") + return + + # Mostrar panel si no está visible + if not self.latex_panel_visible: + self._show_latex_panel() + messagebox.showinfo("Panel LaTeX", "Panel LaTeX activado para testing") + return + + # NUEVO: Test de contenido HTML actual + test_content_js = """ + console.log('🔍 [CONTENIDO] Test de contenido HTML actual...'); + + // 1. Verificar que el contenedor existe + var container = document.getElementById('equations-container'); + console.log('🔍 [CONTENIDO] Contenedor encontrado:', container !== null); + + if (container) { + console.log('🔍 [CONTENIDO] Contenido del contenedor:'); + console.log(container.innerHTML.substring(0, 500) + '...'); + + // 2. Contar elementos específicos + var equationBlocks = container.querySelectorAll('.equation-block'); + var mathDisplays = container.querySelectorAll('.math-display'); + var infoMessages = container.querySelectorAll('.info-message'); + + console.log('🔍 [CONTENIDO] Bloques de ecuación:', equationBlocks.length); + console.log('🔍 [CONTENIDO] Displays matemáticos:', mathDisplays.length); + console.log('🔍 [CONTENIDO] Mensajes de info:', infoMessages.length); + + // 3. Log de contenido de cada ecuación + for (var i = 0; i < Math.min(mathDisplays.length, 3); i++) { + console.log('🔍 [CONTENIDO] Ecuación ' + i + ':', mathDisplays[i].innerHTML.substring(0, 100)); + } + + // 4. Verificar si hay contenido renderizado por MathJax + var mjxElements = container.querySelectorAll('mjx-container, .MathJax, mjx-math'); + console.log('🔍 [CONTENIDO] Elementos MathJax renderizados:', mjxElements.length); + + // 5. Actualizar status con información de contenido + var statusEl = document.getElementById('mathjax-status'); + if (statusEl) { + var info = 'Contenido: ' + equationBlocks.length + ' ecuaciones, ' + mjxElements.length + ' renderizadas'; + statusEl.innerHTML = '🔍 ' + info; + statusEl.style.color = '#4fc3f7'; + } + } else { + console.log('❌ [CONTENIDO] Contenedor no encontrado'); + } + + console.log('🔍 [CONTENIDO] Test de contenido completado'); + """ + + # Ejecutar test de contenido primero + self.logger.debug("🧪 Ejecutando test de contenido HTML en tkinterweb...") + content_success = self._eval_js_tkinterweb(test_content_js) + + # Luego ejecutar test general + test_js = """ + console.log('🧪 [TEST] Iniciando test específico tkinterweb...'); + + // 1. Verificar estado general + console.log('🧪 [TEST] MathJax disponible:', typeof window.MathJax !== 'undefined'); + console.log('🧪 [TEST] typesetPromise disponible:', typeof window.MathJax !== 'undefined' && typeof window.MathJax.typesetPromise !== 'undefined'); + + // 2. Verificar funciones específicas + console.log('🧪 [TEST] renderizarParaTkinterweb:', typeof renderizarParaTkinterweb); + console.log('🧪 [TEST] forzarRenderizadoTkinterweb:', typeof forzarRenderizadoTkinterweb); + + // 3. Contar elementos GLOBALES + var mathElements = document.querySelectorAll('.math-display'); + var mjxElements = document.querySelectorAll('mjx-container, .MathJax, mjx-math'); + console.log('🧪 [TEST] Elementos .math-display GLOBALES:', mathElements.length); + console.log('🧪 [TEST] Elementos MathJax renderizados GLOBALES:', mjxElements.length); + + // 4. Test de renderizado forzado + if (typeof forzarRenderizadoTkinterweb === 'function') { + console.log('🧪 [TEST] Ejecutando renderizado forzado...'); + forzarRenderizadoTkinterweb(); + } else { + console.log('⚠️ [TEST] Función de renderizado forzado no disponible'); + } + + console.log('🧪 [TEST] Test completado'); + """ + + success = self._eval_js_tkinterweb(test_js) + + # Determinar estado final + if content_success and success: + status_msg = "Test completo ejecutado correctamente." + elif content_success: + status_msg = "Test de contenido ejecutado, JavaScript limitado." + else: + status_msg = "Tests limitados - JavaScript no disponible." + + # Información adicional del sistema + equation_count = len(self._latex_equations) if hasattr(self, '_latex_equations') else 0 + + messagebox.showinfo( + "Test tkinterweb", + f"{status_msg}\n\n" + f"JavaScript disponible: {self._js_available}\n" + f"tkinterweb disponible: {self._webview_available}\n" + f"Ecuaciones en memoria: {equation_count}\n\n" + "Tests ejecutados:\n" + f"✓ Contenido HTML: {'Sí' if content_success else 'No'}\n" + f"✓ Funciones MathJax: {'Sí' if success else 'No'}\n\n" + "Para ver logs detallados:\n" + "• Revisa la consola de la aplicación\n" + "• O usa 'Abrir HTML en Navegador' + F12" + ) + + except Exception as e: + self.logger.error(f"Error en test tkinterweb: {e}") + messagebox.showerror("Error", f"Error en test tkinterweb:\n{e}") + + def _save_html_debug_copy(self, html_content): + """Guarda una copia del HTML generado para debugging en navegador""" + try: + # Usar nombre simple para fácil acceso + filename = "latex_debug.html" + + # Guardar en el directorio de la aplicación + with open(filename, 'w', encoding='utf-8') as f: + f.write(html_content) + + # Actualizar referencia al último archivo generado + self._last_debug_html = filename + + self.logger.debug(f"🔍 HTML de debug guardado: {filename}") + + except Exception as e: + self.logger.error(f"❌ Error guardando HTML de debug: {e}") + + def _open_debug_html_in_browser(self): + """Abre el último HTML de debug en el navegador del sistema""" + try: + if not hasattr(self, '_last_debug_html') or not self._last_debug_html: + messagebox.showwarning("Sin archivo", "No hay archivo HTML de debug disponible") + return + + if not os.path.exists(self._last_debug_html): + messagebox.showerror("Archivo no encontrado", f"El archivo {self._last_debug_html} no existe") + return + + import webbrowser + file_path = os.path.abspath(self._last_debug_html) + file_url = f"file:///{file_path.replace('\\', '/')}" + + webbrowser.open(file_url) + + messagebox.showinfo( + "HTML Abierto", + f"Archivo abierto en navegador:\n{self._last_debug_html}\n\n" + f"Presiona F12 para ver la consola del navegador y depurar MathJax.\n\n" + f"NUEVA CONFIGURACIÓN:\n" + f"• Sin polyfill.io (evita errores SSL)\n" + f"• Diagnóstico mejorado en consola\n" + f"• Función global: forzarRenderizado()" + ) + + except Exception as e: + messagebox.showerror("Error", f"Error abriendo archivo en navegador:\n{e}") + + def _show_latex_panel_status(self): + """Muestra el estado actual del panel LaTeX""" + try: + # Información básica + panel_exists = hasattr(self, 'latex_panel') + panel_visible = hasattr(self, 'latex_panel_visible') and self.latex_panel_visible + webview_exists = hasattr(self, 'latex_webview') and self.latex_webview + equations_count = len(self._latex_equations) if hasattr(self, '_latex_equations') else 0 + + # Información sobre las ecuaciones + equations_info = "" + if hasattr(self, '_latex_equations') and self._latex_equations: + equations_info = "\n\nECUACIONES EN MEMORIA:\n" + for i, eq in enumerate(self._latex_equations[:5]): # Mostrar solo las primeras 5 + eq_type = eq.get('type', 'unknown') + latex_content = eq.get('latex', '') + equations_info += f"{i+1}. [{eq_type}] {latex_content[:50]}...\n" + if len(self._latex_equations) > 5: + equations_info += f"... y {len(self._latex_equations) - 5} más\n" + + # Estado de archivos + debug_file_exists = os.path.exists("latex_debug.html") + debug_file_info = "" + if debug_file_exists: + stat = os.stat("latex_debug.html") + import datetime + mod_time = datetime.datetime.fromtimestamp(stat.st_mtime) + debug_file_info = f"\nArchivo debug: latex_debug.html\nÚltima modificación: {mod_time.strftime('%H:%M:%S')}" + + status_message = f"""ESTADO DEL PANEL LATEX + +COMPONENTES: +• Panel creado: {'✓' if panel_exists else '✗'} +• Panel visible: {'✓' if panel_visible else '✗'} +• WebView tkinterweb: {'✓' if webview_exists else '✗'} +• JavaScript disponible: {'✓' if self._js_available else '✗'} + +CONTENIDO: +• Ecuaciones en memoria: {equations_count} +• Archivo debug existe: {'✓' if debug_file_exists else '✗'}{debug_file_info} + +WEBVIEW TYPE: {getattr(self, '_webview_type', 'No definido')} +WEBVIEW AVAILABLE: {getattr(self, '_webview_available', 'No definido')}{equations_info} + +PARA SOLUCIONAR: +1. Si las ecuaciones están en memoria pero no se ven: + → Usa 'Test tkinterweb' para diagnosticar +2. Si el archivo debug tiene contenido pero tkinterweb no: + → Puede ser problema de renderizado en tkinterweb +3. Revisa el archivo debug con 'Abrir HTML en Navegador'""" + + # Crear ventana de estado + status_window = tk.Toplevel(self.root) + status_window.title("Estado Panel LaTeX") + status_window.geometry("600x500") + status_window.configure(bg="#2b2b2b") + + text_widget = scrolledtext.ScrolledText( + status_window, + font=("Consolas", 10), + bg="#1e1e1e", + fg="#d4d4d4", + wrap=tk.WORD + ) + text_widget.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) + + text_widget.insert("1.0", status_message) + text_widget.config(state="disabled") + + # Botón de cerrar + close_btn = tk.Button( + status_window, + text="Cerrar", + command=status_window.destroy, + bg="#3c3c3c", + fg="white", + relief=tk.FLAT + ) + close_btn.pack(pady=5) + + except Exception as e: + self.logger.error(f"Error mostrando estado del panel: {e}") + messagebox.showerror("Error", f"Error mostrando estado:\n{e}") + + def _force_html_update(self): + """Fuerza actualización del panel LaTeX y generación de nuevo HTML de debug""" + try: + if hasattr(self, '_latex_equations') and self._latex_equations: + self.logger.info("🔄 Forzando actualización del panel LaTeX...") + + # Forzar actualización del panel + if self.latex_panel_visible: + self._update_latex_panel() + else: + # Si no está visible, mostrarlo temporalmente para generar HTML + self._show_latex_panel() + self.root.after(1000, lambda: self._update_latex_panel()) + + messagebox.showinfo( + "Actualización Forzada", + f"Panel LaTeX actualizado.\n\n" + f"Ecuaciones en panel: {len(self._latex_equations)}\n" + f"Nuevo HTML generado: latex_debug.html\n\n" + f"Usa 'Abrir HTML en Navegador' para ver el resultado." + ) + else: + messagebox.showwarning( + "Sin contenido", + "No hay ecuaciones en el panel LaTeX para actualizar.\n\n" + "Escribe algunas ecuaciones en la calculadora primero." + ) + + except Exception as e: + self.logger.error(f"Error forzando actualización: {e}") + messagebox.showerror("Error", f"Error forzando actualización:\n{e}") + + def _add_to_latex_panel(self, content_type: str, latex_content: str): + """Añade una ecuación al panel LaTeX""" + self.logger.debug(f"🔧 AÑADIENDO al panel LaTeX: tipo='{content_type}', contenido='{latex_content[:100]}...'") + + if not hasattr(self, '_latex_equations'): + self._latex_equations = [] + self.logger.debug(" -> Lista de ecuaciones inicializada") + + # Crear datos de la ecuación + equation_data = { + 'type': content_type, + 'latex': latex_content, + 'original': latex_content, # Guardar contenido original también + 'timestamp': time.time() + } + + before_count = len(self._latex_equations) + self._latex_equations.append(equation_data) + after_count = len(self._latex_equations) + + # Limitar número de ecuaciones (opcional) + max_equations = 50 + if len(self._latex_equations) > max_equations: + self._latex_equations = self._latex_equations[-max_equations:] + self.logger.debug(f" -> Lista limitada a {max_equations} ecuaciones") + + self.logger.debug(f"➕ Ecuación añadida: {content_type} (Total: {before_count} -> {after_count})") + + # Actualizar indicador visual + self._update_content_indicator() + + # IMPORTANTE: Actualizar panel si está visible + panel_visible = hasattr(self, 'latex_panel_visible') and self.latex_panel_visible + + self.logger.debug(f"🔍 Estado del panel: existe={hasattr(self, 'latex_panel_visible')}, visible={panel_visible}") + + if panel_visible: + self.logger.debug("🔄 Panel visible - FORZANDO actualización de contenido...") + self._update_latex_panel() + else: + self.logger.debug("👁️ Panel no visible - saltando actualización (ecuación guardada para cuando se abra)") + + self.logger.debug(f"✅ Proceso de añadir ecuación COMPLETADO") + self.logger.debug(f"📊 Estado final: {len(self._latex_equations)} ecuaciones en total") def main(): diff --git a/test.py b/test.py new file mode 100644 index 0000000..a522b3f --- /dev/null +++ b/test.py @@ -0,0 +1,37 @@ +import webview + +def create_webview_app(): + html_content = """ + + + + + + + + + +

MathJax con webview

+
+

Ecuación simple: $x^2 + y^2 = z^2$

+

Ecuación compleja:

+ $$\\sum_{n=1}^{\\infty} \\frac{1}{n^2} = \\frac{\\pi^2}{6}$$ +
+ + + """ + + webview.create_window('MathJax App', html=html_content, width=800, height=600) + webview.start() + + +# Función principal para elegir método +if __name__ == "__main__": + create_webview_app()