Mejora del panel LaTeX y ajustes en la configuración de la interfaz. Se implementa un sistema para verificar la disponibilidad de MathJax y se optimiza la gestión de ecuaciones pendientes. Se actualizan estilos y se ajusta la geometría de la ventana. Se añade un wrapper para la función 'solve' que convierte cadenas a símbolos automáticamente, mejorando la resolución de ecuaciones.

This commit is contained in:
Miguel 2025-06-11 10:19:14 +02:00
parent b7e35d1ae3
commit 68bed8937a
5 changed files with 265 additions and 61 deletions

View File

@ -1,12 +1,20 @@
x=t**2+5/m
t=?
m=3
x=4
t=?
solve(t)
form=solve(t)
ip=IP4(10.1.1.1,20)
ip.BroadcastAddress()
# Test LATEX
$$Brix = \frac{Brix_{syrup} \cdot \delta_{syrup} + (Brix_{water} \cdot \delta_{water} \cdot Rateo)}{\delta_{syrup} + \delta_{water} \cdot Rateo}$$
$$Brix = \frac{Solid_Weight}{Total_Weight}$$
$$\delta = \frac{W}{V} \Rightarrow W = \delta \cdot V$$
$$Brix_{Bev} = \frac{Brix_{syr} + Brix_{H_2O} \cdot R_M}{R_M + 1}$$

View File

@ -1,15 +1,15 @@
{
"window_geometry": {
"x": 732,
"y": 205,
"width": 741,
"x": 332,
"y": 170,
"width": 1353,
"height": 700
},
"debug_mode": false,
"latex_panel_visible": true,
"sash_pos_x": 450,
"splitter_sizes": [
210,
155
357,
472
]
}

View File

@ -116,8 +116,16 @@ class LatexPanel(QWidget):
super().__init__(parent)
self.equations = []
self._webview_available = False
self._mathjax_ready = False
self._pending_equations = []
self._parent_calculator = parent
self._setup_ui()
# Timer para verificar si MathJax está listo
self._mathjax_check_timer = QTimer()
self._mathjax_check_timer.timeout.connect(self._check_mathjax_ready)
self._mathjax_check_timer.start(500) # Verificar cada 500ms
def _setup_ui(self):
"""Configura la UI del panel"""
layout = QVBoxLayout(self)
@ -212,8 +220,16 @@ class LatexPanel(QWidget):
processEscapes: true
},
chtml: {
scale: 1.1,
scale: 0.9,
minScale: 0.5
},
startup: {
ready: function () {
MathJax.startup.defaultReady();
// Notificar que MathJax está listo
window.mathJaxReady = true;
console.log('MathJax completamente cargado');
}
}
};
</script>
@ -223,19 +239,18 @@ class LatexPanel(QWidget):
color: #d4d4d4;
font-family: 'Segoe UI', Arial, sans-serif;
margin: 0;
padding: 20px;
line-height: 1.6;
padding: 8px;
line-height: 1.2;
}
.equation-block {
background: rgba(45, 45, 48, 0.8);
border-left: 4px solid;
margin: 12px 0;
padding: 15px 20px;
border-radius: 8px;
transition: all 0.2s ease;
border-left: 3px solid;
margin: 4px 0;
padding: 6px 12px;
border-radius: 4px;
transition: all 0.1s ease;
}
.equation-block:hover {
transform: translateY(-1px);
background: rgba(45, 45, 48, 0.9);
}
.comment { border-left-color: #6a9955; }
@ -243,35 +258,49 @@ class LatexPanel(QWidget):
.equation { border-left-color: #c586c0; }
.symbolic { border-left-color: #9cdcfe; }
.equation-type {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
margin-bottom: 8px;
opacity: 0.8;
.math-content {
margin: 2px 0;
font-size: 14px;
}
.comment-text {
font-style: italic;
color: #6a9955;
font-size: 12px;
margin: 0;
}
.comment .equation-type { color: #6a9955; }
.assignment .equation-type { color: #dcdcaa; }
.equation .equation-type { color: #c586c0; }
.symbolic .equation-type { color: #9cdcfe; }
.info-message {
text-align: center;
padding: 40px 20px;
color: #888;
font-style: italic;
padding: 20px;
color: #666;
font-size: 12px;
}
/* Reducir espaciado de MathJax */
.MathJax {
font-size: 0.9em !important;
}
mjx-math {
margin: 1px 0 !important;
}
mjx-container {
margin: 2px 0 !important;
}
</style>
</head>
<body>
<div id="equations-container">
<div class="info-message">
📐 Panel de Ecuaciones LaTeX<br/>
<small>Las ecuaciones aparecerán aquí automáticamente</small>
Panel de Ecuaciones LaTeX
</div>
</div>
<script>
window.mathJaxReady = false;
function addEquation(type, content) {
var container = document.getElementById('equations-container');
@ -282,25 +311,21 @@ class LatexPanel(QWidget):
var equation = document.createElement('div');
equation.className = 'equation-block ' + type;
var typeLabel = document.createElement('div');
typeLabel.className = 'equation-type';
typeLabel.textContent = type.charAt(0).toUpperCase() + type.slice(1);
var mathContent = document.createElement('div');
mathContent.className = 'math-content';
if (type === 'comment') {
mathContent.style.fontStyle = 'italic';
mathContent.style.color = '#6a9955';
mathContent.className = 'comment-text';
mathContent.textContent = content;
} else {
mathContent.innerHTML = '$$' + content + '$$';
}
equation.appendChild(typeLabel);
equation.appendChild(mathContent);
container.appendChild(equation);
// Re-renderizar MathJax
if (window.MathJax && type !== 'comment') {
// Re-renderizar MathJax solo si está listo
if (window.MathJax && window.mathJaxReady && type !== 'comment') {
MathJax.typesetPromise([equation]).catch(function(err) {
console.error('MathJax error:', err);
});
@ -309,21 +334,67 @@ class LatexPanel(QWidget):
function clearEquations() {
var container = document.getElementById('equations-container');
container.innerHTML = '<div class="info-message">📐 Panel de Ecuaciones LaTeX<br/><small>Las ecuaciones aparecerán aquí automáticamente</small></div>';
container.innerHTML = '<div class="info-message">Panel de Ecuaciones LaTeX</div>';
}
// Función para notificar que está listo para renderizar
function triggerInitialRender() {
if (window.mathJaxReady) {
// Notificar a Python que MathJax está listo
console.log('MathJax listo para renderizado inicial');
return true;
}
return false;
}
</script>
</body>
</html>"""
def _check_mathjax_ready(self):
"""Verifica si MathJax está listo y renderiza ecuaciones pendientes"""
if not self._webview_available:
return
# Verificar si MathJax está listo
self.webview.page().runJavaScript(
"window.mathJaxReady || false;",
self._on_mathjax_ready_check
)
def _on_mathjax_ready_check(self, ready):
"""Callback cuando se verifica el estado de MathJax"""
if ready and not self._mathjax_ready:
self._mathjax_ready = True
self._mathjax_check_timer.stop()
logging.debug("✅ MathJax listo, procesando ecuaciones pendientes")
# Renderizar ecuaciones pendientes
for eq in self._pending_equations:
self._add_equation_to_webview(eq['type'], eq['content'])
self._pending_equations.clear()
# Trigger initial render si hay un calculador padre
if self._parent_calculator and hasattr(self._parent_calculator, '_trigger_initial_latex_render'):
self._parent_calculator._trigger_initial_latex_render()
def _add_equation_to_webview(self, eq_type: str, content: str):
"""Añade una ecuación directamente al webview"""
if self._webview_available:
escaped_content = content.replace('\\', '\\\\').replace("'", "\\'").replace('"', '\\"')
js_code = f"addEquation('{eq_type}', '{escaped_content}');"
self.webview.page().runJavaScript(js_code)
def add_equation(self, eq_type: str, content: str):
"""Añade una ecuación al panel"""
self.equations.append({'type': eq_type, 'content': content})
if self._webview_available:
# Escapar contenido para JavaScript
escaped_content = content.replace('\\', '\\\\').replace("'", "\\'").replace('"', '\\"')
js_code = f"addEquation('{eq_type}', '{escaped_content}');"
self.webview.page().runJavaScript(js_code)
if self._mathjax_ready:
# MathJax está listo, renderizar inmediatamente
self._add_equation_to_webview(eq_type, content)
else:
# MathJax no está listo, guardar para después
self._pending_equations.append({'type': eq_type, 'content': content})
else:
# Actualizar HTML en text browser
self._update_text_browser()
@ -331,6 +402,7 @@ class LatexPanel(QWidget):
def clear_equations(self):
"""Limpia todas las ecuaciones"""
self.equations.clear()
self._pending_equations.clear()
if self._webview_available:
self.webview.page().runJavaScript("clearEquations();")
@ -341,28 +413,49 @@ class LatexPanel(QWidget):
"""Actualiza el contenido del text browser (fallback)"""
html_parts = ["""
<style>
body { background-color: #1a1a1a; color: #d4d4d4; font-family: 'Consolas'; }
.equation { margin: 10px 0; padding: 10px; background: #2d2d2d; border-left: 3px solid #80c7f7; border-radius: 3px; }
.comment { border-left-color: #6a9955; font-style: italic; }
body {
background-color: #1a1a1a;
color: #d4d4d4;
font-family: 'Consolas';
margin: 0;
padding: 8px;
line-height: 1.2;
}
.equation {
margin: 4px 0;
padding: 6px 12px;
background: rgba(45, 45, 48, 0.8);
border-left: 3px solid #80c7f7;
border-radius: 4px;
font-size: 12px;
}
.comment {
border-left-color: #6a9955;
font-style: italic;
color: #6a9955;
font-size: 11px;
}
.assignment { border-left-color: #dcdcaa; }
.equation-type { border-left-color: #c586c0; }
.symbolic { border-left-color: #9cdcfe; }
.type-label { font-size: 10px; color: #808080; margin-bottom: 5px; }
code {
font-family: 'Consolas';
font-size: 11px;
color: #d4d4d4;
}
</style>
"""]
for eq in self.equations:
eq_type = eq['type']
content = eq['content']
type_label = eq_type.capitalize()
css_class = eq_type
if eq_type == 'comment':
html_parts.append(f'<div class="equation {css_class}"><div class="type-label">{type_label}</div>{content}</div>')
html_parts.append(f'<div class="equation {css_class}">{content}</div>')
else:
# Para ecuaciones matemáticas, mostrar en formato de código
html_parts.append(f'<div class="equation {css_class}"><div class="type-label">{type_label}</div><code>{content}</code></div>')
html_parts.append(f'<div class="equation {css_class}"><code>{content}</code></div>')
self.text_browser.setHtml(''.join(html_parts))
@ -586,7 +679,7 @@ class HybridCalculatorPySide6(QMainWindow):
main_layout.addWidget(self.latex_button)
# Panel LaTeX (inicialmente oculto)
self.latex_panel = LatexPanel()
self.latex_panel = LatexPanel(self)
self.latex_panel.setMinimumWidth(300)
self.latex_panel.setMaximumWidth(500)
@ -890,6 +983,17 @@ class HybridCalculatorPySide6(QMainWindow):
except Exception as e:
self._show_error(f"Error durante evaluación: {e}")
def _trigger_initial_latex_render(self):
"""Activa el renderizado inicial del panel LaTeX cuando MathJax está listo"""
try:
# Solo hacer evaluación inicial si hay contenido en el input
input_content = self.input_text.toPlainText()
if input_content.strip():
logging.debug("🎯 Activando renderizado inicial de LaTeX")
self._evaluate_and_update()
except Exception as e:
logging.error(f"Error en renderizado inicial de LaTeX: {e}")
def _evaluate_lines(self, lines: List[str]):
"""Evalúa múltiples líneas de código"""
output_data = []

View File

@ -80,7 +80,6 @@ class PureAlgebraicEngine:
'sqrt': sp.sqrt, 'abs': sp.Abs,
'pi': sp.pi, 'e': sp.E, 'I': sp.I,
'oo': sp.oo, 'inf': sp.oo,
'solve': self._smart_solve,
'Eq': sp.Eq, 'simplify': sp.simplify,
'expand': sp.expand, 'factor': sp.factor,
'diff': sp.diff, 'integrate': sp.integrate,
@ -98,7 +97,55 @@ class PureAlgebraicEngine:
# 2. TIPOS PERSONALIZADOS REGISTRADOS (CLAVE PARA INSTANCIACIÓN)
registered_types = get_registered_base_context()
# 3. FUNCIONES DE PLOTTING (WRAPPED)
# 3. FUNCIÓN SOLVE ESPECIAL que maneja cadenas como símbolos
def solve_wrapper(*args, **kwargs):
"""Wrapper para solve que convierte cadenas a símbolos automáticamente"""
processed_args = []
for arg in args:
if isinstance(arg, str):
# Si es una cadena, convertir a símbolo
processed_args.append(sp.Symbol(arg))
elif hasattr(arg, 'is_number') and arg.is_number:
# Si es un valor numérico, buscar el símbolo correspondiente
# en symbol_table y verificar contexto
for var_name, var_value in self.symbol_table.items():
if var_value == arg:
# Encontramos una variable con este valor, usar el símbolo
processed_args.append(sp.Symbol(var_name))
break
else:
# Si no se encuentra, intentar usar _smart_solve directo
# que puede manejar valores numéricos
processed_args.append(arg)
else:
processed_args.append(arg)
result = self._smart_solve(*processed_args, **kwargs)
# Si el resultado está vacío, intentar un enfoque alternativo
if hasattr(result, '__len__') and len(result) == 0:
# Caso especial: si solve() devuelve lista vacía,
# intentar resolver como si no hubiera valores asignados
alt_args = []
for arg in args:
if hasattr(arg, 'is_number') and arg.is_number:
# Buscar la variable que tiene este valor
for var_name, var_value in self.symbol_table.items():
if var_value == arg:
alt_args.append(sp.Symbol(var_name))
break
else:
alt_args.append(arg)
else:
alt_args.append(arg)
if alt_args != processed_args:
# Si hay diferencia, reintentar
return self._smart_solve(*alt_args, **kwargs)
return result
# 4. FUNCIONES DE PLOTTING (WRAPPED)
# Wrappers para capturar llamadas de plot y devolver un objeto PlotResult
def plot_wrapper(*args, **kwargs):
# Intentar extraer la expresión original del primer argumento
@ -127,14 +174,15 @@ class PureAlgebraicEngine:
'plot3d_parametric_line': plot3d_parametric_line_wrapper,
}
# 4. COMBINAR TODO EN CONTEXTO UNIFICADO
# 5. COMBINAR TODO EN CONTEXTO UNIFICADO
self.unified_context = {
**sympy_functions,
**registered_types, # IP4, FourBytes, IntBase, etc.
**plotting_functions
**plotting_functions,
'solve': solve_wrapper
}
# 5. VERIFICAR CARGA DE TIPOS PRINCIPALES
# 6. VERIFICAR CARGA DE TIPOS PRINCIPALES
required_classes = ['IP4', 'IP4Mask', 'FourBytes', 'IntBase', 'Hex', 'Bin', 'Dec', 'Chr', 'LaTeX']
missing_classes = [cls for cls in required_classes if cls not in self.unified_context]

44
test_final.py Normal file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""
Test final para verificar que el problema de solve(t) esté solucionado
"""
from main_evaluation_puro import PureAlgebraicEngine
def test_solve_issue():
print("=== TEST FINAL: PROBLEMA SOLVE(T) ===")
engine = PureAlgebraicEngine()
# Secuencia original del usuario
lines = [
'x=t**2+5/m',
'm=3',
'x=4',
't=?',
'solve(t)',
'form=solve(t)'
]
print("Ejecutando secuencia del usuario:")
for i, line in enumerate(lines, 1):
result = engine.evaluate_line(line)
status = "" if result.success else ""
print(f"{i}. {line:<15} -> {result.output} {status}")
# Verificar específicamente el caso problemático
if line == 'form=solve(t)':
if result.output == 'form = []':
print(" ❌ PROBLEMA PERSISTE: form está vacío")
return False
else:
print(" ✅ PROBLEMA SOLUCIONADO: form contiene resultado")
return True
return True
if __name__ == "__main__":
success = test_solve_issue()
if success:
print("\n🎉 PROBLEMA SOLUCIONADO EXITOSAMENTE")
else:
print("\n💥 PROBLEMA PERSISTE")