"""
Content Viewer - Center panel for displaying lesson content
"""
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
from PyQt5.QtCore import Qt, pyqtSignal, QUrl
from pathlib import Path
import markdown
from pymdownx import superfences, arithmatex
from app import config
from app.models import Lesson
from app.utils import VariableWrapper
class ContentViewer(QWidget):
"""Center panel for displaying lesson content with markdown and MathJax"""
# Signals
scroll_position_changed = pyqtSignal(float) # For auto-save
def __init__(self, parent=None):
super().__init__(parent)
self.current_lesson = None
self.markdown_converter = self._init_markdown()
self.variable_wrapper = VariableWrapper()
self.init_ui()
def init_ui(self):
"""Initialize the UI components"""
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
# Lesson title bar
self.title_label = QLabel("No lesson selected")
self.title_label.setStyleSheet(f"""
background-color: {config.COLOR_PRIMARY};
color: white;
font-size: 16pt;
font-weight: bold;
padding: 12px;
""")
self.title_label.setWordWrap(True)
layout.addWidget(self.title_label)
# Web view for content
self.web_view = QWebEngineView()
self.web_view.setPage(QWebEnginePage(self.web_view))
layout.addWidget(self.web_view, 1)
# Load welcome page
self.show_welcome()
def _init_markdown(self):
"""Initialize markdown converter with extensions"""
return markdown.Markdown(
extensions=[
'extra',
'codehilite',
'tables',
'toc',
'pymdownx.arithmatex',
'pymdownx.superfences',
'pymdownx.highlight',
'pymdownx.inlinehilite',
],
extension_configs={
'pymdownx.arithmatex': {
'generic': True
},
'codehilite': {
'css_class': 'highlight',
'linenums': False
}
}
)
def show_welcome(self):
"""Display welcome message"""
html = self._wrap_html("""
Welcome to Tesla Coil Spark Physics Course
Select a lesson from the navigation panel to begin learning.
⚡ Explore the fascinating world of Tesla coils and electromagnetic theory ⚡
""", "Welcome")
self.web_view.setHtml(html)
self.title_label.setText("Welcome")
def load_lesson(self, lesson: Lesson):
"""Load and display a lesson"""
self.current_lesson = lesson
self.title_label.setText(f"{lesson.order}. {lesson.title}")
# Read markdown file
lesson_path = Path(lesson.file_path)
if not lesson_path.exists():
self.show_error(f"Lesson file not found: {lesson.file_path}")
return
try:
with open(lesson_path, 'r', encoding='utf-8') as f:
markdown_content = f.read()
# Convert markdown to HTML
html_content = self.markdown_converter.convert(markdown_content)
# Process custom tags
html_content = self._process_custom_tags(html_content, lesson)
# Wrap variables with tooltips
html_content = self.variable_wrapper.wrap_in_context(html_content)
# Wrap in full HTML document
full_html = self._wrap_html(html_content, lesson.title)
# Load into web view
self.web_view.setHtml(full_html, QUrl.fromLocalFile(str(lesson_path.parent)))
except Exception as e:
self.show_error(f"Error loading lesson: {str(e)}")
def _process_custom_tags(self, html: str, lesson: Lesson) -> str:
"""Process custom tags like {exercise:id} and {image:file}"""
import re
# Process {exercise:id} tags
def replace_exercise(match):
exercise_id = match.group(1)
return f'''
📝 Exercise: {exercise_id}
Interactive exercise will be loaded here
'''
html = re.sub(r'\{exercise:([^}]+)\}', replace_exercise, html)
# Process {image:file} tags
def replace_image(match):
image_file = match.group(1)
image_path = config.IMAGES_DIR / image_file
return f'
'
html = re.sub(r'\{image:([^}]+)\}', replace_image, html)
return html
def _wrap_html(self, content: str, title: str) -> str:
"""Wrap content in full HTML document with styling and MathJax"""
return f"""
{title}
{content}
"""
def show_error(self, message: str):
"""Display an error message"""
html = self._wrap_html(f"""
""", "Error")
self.web_view.setHtml(html)
def get_scroll_position(self) -> float:
"""Get current scroll position (0.0 to 1.0)"""
# This would require JavaScript execution in QWebEngineView
# For now, return 0.0 - can be implemented later
return 0.0
def set_scroll_position(self, position: float):
"""Set scroll position (0.0 to 1.0)"""
# This would require JavaScript execution in QWebEngineView
# For now, do nothing - can be implemented later
pass