import sys import json import os import logging from pathlib import Path from typing import Optional, Dict, Any, List, Tuple from PyQt6.QtWidgets import ( QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QTabWidget, QLabel, QLineEdit, QComboBox, QTextEdit, QPushButton, QStatusBar, QMessageBox, QDialog, QListWidget, QSplitter, QFrame, QSizePolicy, QScrollArea, QFileDialog, QToolTip ) from PyQt6.QtCore import Qt, QSize, pyqtSignal, QTimer from PyQt6.QtGui import QFont, QTextOption, QSyntaxHighlighter, QTextCharFormat, QColor, QTextCursor, QKeyEvent, QTextDocument from PyQt6.QtWebEngineWidgets import QWebEngineView # Configure logging logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logger = logging.getLogger("ProblemCreator") # Optional imports with proper error handling OPTIONAL_DEPENDENCIES = {} try: import markdown OPTIONAL_DEPENDENCIES['markdown'] = True except ImportError: logger.warning("Markdown library not available, using basic conversion") OPTIONAL_DEPENDENCIES['markdown'] = False try: from pygments import lex from pygments.lexers import PythonLexer from pygments.styles import get_style_by_name OPTIONAL_DEPENDENCIES['pygments'] = True except ImportError: logger.warning("Pygments not available, syntax highlighting will be disabled") OPTIONAL_DEPENDENCIES['pygments'] = False class CodeEditor(QTextEdit): """A code editor with syntax highlighting and advanced auto-indentation.""" def __init__(self, parent=None): super().__init__(parent) self.setFont(QFont("Consolas", 10)) self.indent_width = 4 self.setTabStopDistance(QFont("Consolas", 10).pointSize() * self.indent_width) # Setup syntax highlighting if available if OPTIONAL_DEPENDENCIES.get('pygments', False): self.highlighter = PythonHighlighter(self.document()) # Tips for test case editing self.tips = [ "💡 Tip: Use descriptive test method names like test_empty_input()", "💡 Tip: Test edge cases like empty inputs, large inputs, and invalid inputs", "💡 Tip: Use assertEqual for exact matches, assertTrue/False for boolean checks", "💡 Tip: Test both valid and invalid inputs to ensure robustness", "💡 Tip: Consider using setUp() method for common test setup", "💡 Tip: Use parameterized tests if you have many similar test cases", "💡 Tip: Make sure your tests are independent of each other", "💡 Tip: Test not only for correct outputs but also for proper error handling" ] self.current_tip_index = 0 self.tip_timer = QTimer(self) self.tip_timer.timeout.connect(self.show_next_tip) self.tip_timer.start(10000) # Show a new tip every 10 seconds def show_next_tip(self): """Show the next tip in the status bar.""" if self.parent() and hasattr(self.parent().parent().parent().parent(), 'statusBar'): status_bar = self.parent().parent().parent().parent().statusBar() if status_bar: tip = self.tips[self.current_tip_index] status_bar.showMessage(tip) self.current_tip_index = (self.current_tip_index + 1) % len(self.tips) def keyPressEvent(self, event: QKeyEvent): """Handle key press events for auto-indentation and pairing.""" key = event.key() modifiers = event.modifiers() cursor = self.textCursor() # Tab key if key == Qt.Key.Key_Tab: if cursor.hasSelection(): self.indentSelection() else: # Insert spaces cursor.insertText(" " * self.indent_width) return # Shift+Tab key elif key == Qt.Key.Key_Backtab: if cursor.hasSelection(): self.dedentSelection() else: self.dedentLine() return # Return key elif key == Qt.Key.Key_Return: # Get current line cursor.movePosition(QTextCursor.MoveOperation.StartOfLine) cursor.movePosition(QTextCursor.MoveOperation.EndOfLine, QTextCursor.MoveMode.KeepAnchor) line_text = cursor.selectedText() # Calculate indentation indent = len(line_text) - len(line_text.lstrip()) # Check if line ends with colon ends_with_colon = line_text.rstrip().endswith(':') # Insert newline with indentation cursor = self.textCursor() cursor.insertText("\n" + " " * indent) # Add extra indentation if line ended with colon if ends_with_colon: cursor.insertText(" " * self.indent_width) return # Auto-pairing elif key == Qt.Key.Key_ParenLeft: cursor.insertText("()") cursor.movePosition(QTextCursor.MoveOperation.Left) self.setTextCursor(cursor) return elif key == Qt.Key.Key_BracketLeft: cursor.insertText("[]") cursor.movePosition(QTextCursor.MoveOperation.Left) self.setTextCursor(cursor) return elif key == Qt.Key.Key_BraceLeft: cursor.insertText("{}") cursor.movePosition(QTextCursor.MoveOperation.Left) self.setTextCursor(cursor) return elif key == Qt.Key.Key_QuoteDbl: cursor.insertText('""') cursor.movePosition(QTextCursor.MoveOperation.Left) self.setTextCursor(cursor) return elif key == Qt.Key.Key_Apostrophe: cursor.insertText("''") cursor.movePosition(QTextCursor.MoveOperation.Left) self.setTextCursor(cursor) return elif key == Qt.Key.Key_Colon and modifiers == Qt.KeyboardModifier.NoModifier: # Check if we're at the end of the line cursor.movePosition(QTextCursor.MoveOperation.EndOfLine) if self.textCursor().position() == cursor.position(): cursor.insertText(":") return # Default behavior super().keyPressEvent(event) def indentSelection(self): """Indent all selected lines.""" cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() # Move to start of selection cursor.setPosition(start) cursor.movePosition(QTextCursor.MoveOperation.StartOfLine) # Indent each line in selection while cursor.position() <= end: cursor.insertText(" " * self.indent_width) end += self.indent_width if not cursor.movePosition(QTextCursor.MoveOperation.Down): break # Restore selection cursor.setPosition(start) cursor.setPosition(end, QTextCursor.MoveMode.KeepAnchor) self.setTextCursor(cursor) def dedentSelection(self): """Dedent all selected lines.""" cursor = self.textCursor() start = cursor.selectionStart() end = cursor.selectionEnd() # Move to start of selection cursor.setPosition(start) cursor.movePosition(QTextCursor.MoveOperation.StartOfLine) # Dedent each line in selection while cursor.position() <= end: # Check for spaces at beginning of line line_start = cursor.position() cursor.movePosition(QTextCursor.MoveOperation.EndOfLine, QTextCursor.MoveMode.KeepAnchor) line_text = cursor.selectedText() # Count leading spaces leading_spaces = min(len(line_text) - len(line_text.lstrip()), self.indent_width) if leading_spaces > 0: # Remove leading spaces cursor.setPosition(line_start) cursor.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, leading_spaces) cursor.removeSelectedText() end -= leading_spaces if not cursor.movePosition(QTextCursor.MoveOperation.Down): break # Restore selection cursor.setPosition(max(0, start - self.indent_width)) cursor.setPosition(max(0, end - self.indent_width), QTextCursor.MoveMode.KeepAnchor) self.setTextCursor(cursor) def dedentLine(self): """Dedent the current line.""" cursor = self.textCursor() cursor.movePosition(QTextCursor.MoveOperation.StartOfLine) # Check for spaces at beginning of line line_start = cursor.position() cursor.movePosition(QTextCursor.MoveOperation.EndOfLine, QTextCursor.MoveMode.KeepAnchor) line_text = cursor.selectedText() # Count leading spaces leading_spaces = min(len(line_text) - len(line_text.lstrip()), self.indent_width) if leading_spaces > 0: # Remove leading spaces cursor.setPosition(line_start) cursor.movePosition(QTextCursor.MoveOperation.Right, QTextCursor.MoveMode.KeepAnchor, leading_spaces) cursor.removeSelectedText() class PythonHighlighter(QSyntaxHighlighter): """Syntax highlighter for Python code using Pygments.""" def __init__(self, document): super().__init__(document) self._setup_formats() def _setup_formats(self): """Setup text formats for different token types.""" self.formats = {} # Define syntax highlighting formats keyword_format = QTextCharFormat() keyword_format.setForeground(QColor("#0000FF")) keyword_format.setFontWeight(QFont.Weight.Bold) self.formats['keyword'] = keyword_format string_format = QTextCharFormat() string_format.setForeground(QColor("#008000")) self.formats['string'] = string_format comment_format = QTextCharFormat() comment_format.setForeground(QColor("#808080")) comment_format.setFontItalic(True) self.formats['comment'] = comment_format function_format = QTextCharFormat() function_format.setForeground(QColor("#000080")) function_format.setFontWeight(QFont.Weight.Bold) self.formats['function'] = function_format number_format = QTextCharFormat() number_format.setForeground(QColor("#FF8C00")) self.formats['number'] = number_format # Python keywords self.keywords = [ 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'False', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'None', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'True', 'try', 'while', 'with', 'yield' ] # unittest keywords self.unittest_keywords = [ 'TestCase', 'setUp', 'tearDown', 'setUpClass', 'tearDownClass', 'assertEqual', 'assertTrue', 'assertFalse', 'assertRaises', 'assertAlmostEqual', 'assertNotEqual', 'assertIn', 'assertNotIn', 'assertIs', 'assertIsNot', 'assertIsNone', 'assertIsNotNone', 'assertIsInstance', 'assertNotIsInstance', 'assertDictEqual', 'assertListEqual', 'assertTupleEqual', 'assertSetEqual', 'assertSequenceEqual', 'assertMultiLineEqual', 'assertGreater', 'assertGreaterEqual', 'assertLess', 'assertLessEqual', 'assertRegex', 'assertNotRegex', 'assertCountEqual' ] def highlightBlock(self, text): """Apply syntax highlighting to the current text block.""" # Check if we should use pygments if OPTIONAL_DEPENDENCIES.get('pygments', False): self._highlight_with_pygments(text) else: self._highlight_with_basic_rules(text) def _highlight_with_pygments(self, text): """Use pygments for syntax highlighting if available.""" try: # Get the text from the current block block = self.currentBlock() start_pos = block.position() end_pos = start_pos + len(text) full_text = self.document().toPlainText() # Lex the code and apply formats for token, value in lex(full_text, PythonLexer()): token_str = str(token) token_start = full_text.find(value, start_pos) # Skip if token is not in current block if token_start < start_pos or token_start >= end_pos: continue # Calculate length within current block token_len = min(len(value), end_pos - token_start) # Apply appropriate format if 'Keyword' in token_str: self.setFormat(token_start - start_pos, token_len, self.formats['keyword']) elif 'String' in token_str: self.setFormat(token_start - start_pos, token_len, self.formats['string']) elif 'Comment' in token_str: self.setFormat(token_start - start_pos, token_len, self.formats['comment']) elif 'Name' in token_str and 'Function' in token_str: self.setFormat(token_start - start_pos, token_len, self.formats['function']) elif 'Number' in token_str: self.setFormat(token_start - start_pos, token_len, self.formats['number']) except Exception as e: logger.error(f"Error during pygments highlighting: {e}") # Fall back to basic highlighting self._highlight_with_basic_rules(text) def _highlight_with_basic_rules(self, text): """Use basic rules for syntax highlighting.""" # Highlight keywords for keyword in self.keywords + self.unittest_keywords: pattern = r'\b' + keyword + r'\b' index = 0 while index < len(text): index = text.find(keyword, index) if index == -1: break # Check if it's really a word (not part of another word) if (index == 0 or not text[index-1].isalnum()) and \ (index + len(keyword) >= len(text) or not text[index + len(keyword)].isalnum()): if keyword in self.keywords: self.setFormat(index, len(keyword), self.formats['keyword']) else: self.setFormat(index, len(keyword), self.formats['function']) index += len(keyword) # Highlight strings import re string_pattern = re.compile(r'(\".*?\")|(\'.*?\')') for match in string_pattern.finditer(text): start, end = match.span() self.setFormat(start, end - start, self.formats['string']) # Highlight comments comment_pattern = re.compile(r'#.*') for match in comment_pattern.finditer(text): start, end = match.span() self.setFormat(start, end - start, self.formats['comment']) # Highlight numbers number_pattern = re.compile(r'\b\d+\b') for match in number_pattern.finditer(text): start, end = match.span() self.setFormat(start, end - start, self.formats['number']) class MarkdownEditor(QWidget): """A markdown editor with live preview.""" def __init__(self, parent=None): super().__init__(parent) # Create split view self.splitter = QSplitter(Qt.Orientation.Horizontal) layout = QVBoxLayout(self) layout.addWidget(self.splitter) # Left side - text editor self.editor = QTextEdit() self.editor.setFont(QFont("Consolas", 10)) self.editor.textChanged.connect(self.update_preview) self.splitter.addWidget(self.editor) # Right side - preview self.preview = QWebEngineView() self.splitter.addWidget(self.preview) # Set initial sizes self.splitter.setSizes([400, 400]) def update_preview(self): """Update the markdown preview.""" # Get the markdown text markdown_text = self.editor.toPlainText() # Convert to HTML html_content = self._markdown_to_html(markdown_text) # Update the preview self.preview.setHtml(html_content) def _markdown_to_html(self, text): """Convert markdown text to HTML.""" if OPTIONAL_DEPENDENCIES.get('markdown', False): # Use the markdown library if available html = markdown.markdown(text) else: # Fallback to basic conversion html = text html = html.replace("# ", "

").replace("\n# ", "

\n

") + "

" html = html.replace("## ", "

").replace("\n## ", "

\n

") + "

" html = html.replace("### ", "

").replace("\n### ", "

\n

") + "

" html = html.replace("**", "").replace("**", "") html = html.replace("*", "").replace("*", "") html = html.replace("`", "").replace("`", "") html = html.replace("\n", "
") # Wrap in proper HTML structure return f""" {html} """ def setPlainText(self, text): """Set the editor text.""" self.editor.setPlainText(text) def toPlainText(self): """Get the editor text.""" return self.editor.toPlainText() class LoadProblemDialog(QDialog): """Dialog for loading existing problems.""" def __init__(self, problems, parent=None): super().__init__(parent) self.setWindowTitle("Load Existing Problem") self.setModal(True) self.setMinimumSize(400, 300) layout = QVBoxLayout(self) label = QLabel("Select a problem to load:") label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) layout.addWidget(label) self.list_widget = QListWidget() self.list_widget.addItems(sorted(problems)) layout.addWidget(self.list_widget) button_layout = QHBoxLayout() self.load_button = QPushButton("Load") self.load_button.clicked.connect(self.accept) button_layout.addWidget(self.load_button) self.cancel_button = QPushButton("Cancel") self.cancel_button.clicked.connect(self.reject) button_layout.addWidget(self.cancel_button) layout.addLayout(button_layout) def selected_problem(self): """Get the selected problem name.""" items = self.list_widget.selectedItems() return items[0].text() if items else None class ProblemCreatorApp(QMainWindow): """Main application for creating coding problems.""" def __init__(self): super().__init__() self.setWindowTitle("Coding Problem Creator") self.setGeometry(100, 100, 1200, 900) # Set default paths self.base_path = Path("src/problems") # Initialize UI self.create_widgets() self.statusBar().showMessage("Ready to create a new problem...") def create_widgets(self): """Create all UI widgets.""" # Central widget central_widget = QWidget() self.setCentralWidget(central_widget) # Main layout main_layout = QVBoxLayout(central_widget) # Create tab widget self.tab_widget = QTabWidget() main_layout.addWidget(self.tab_widget) # Problem Info tab self.info_tab = QWidget() self.tab_widget.addTab(self.info_tab, "Problem Info") self.create_info_tab() # Markdown Description tab self.markdown_tab = QWidget() self.tab_widget.addTab(self.markdown_tab, "Markdown Description") self.create_markdown_tab() # Test Code tab self.test_tab = QWidget() self.tab_widget.addTab(self.test_tab, "Test Code") self.create_test_tab() # Buttons at the bottom button_layout = QHBoxLayout() self.create_button = QPushButton("Create Problem") self.create_button.clicked.connect(self.create_problem) button_layout.addWidget(self.create_button) self.clear_button = QPushButton("Clear All") self.clear_button.clicked.connect(self.clear_all) button_layout.addWidget(self.clear_button) self.load_button = QPushButton("Load Existing") self.load_button.clicked.connect(self.load_existing) button_layout.addWidget(self.load_button) main_layout.addLayout(button_layout) def create_info_tab(self): """Create the Problem Info tab.""" layout = QVBoxLayout(self.info_tab) # Title title_label = QLabel("Coding Problem Creator") title_font = QFont("Arial", 16, QFont.Weight.Bold) title_label.setFont(title_font) layout.addWidget(title_label) # Problem Name name_layout = QHBoxLayout() name_label = QLabel("Problem Name:") name_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) name_layout.addWidget(name_label) self.problem_name = QLineEdit() self.problem_name.setFont(QFont("Arial", 10)) name_layout.addWidget(self.problem_name) layout.addLayout(name_layout) # Difficulty difficulty_layout = QHBoxLayout() difficulty_label = QLabel("Difficulty:") difficulty_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) difficulty_layout.addWidget(difficulty_label) self.difficulty = QComboBox() self.difficulty.addItems(["easy", "medium", "hard"]) self.difficulty.setCurrentText("medium") difficulty_layout.addWidget(self.difficulty) difficulty_layout.addStretch() layout.addLayout(difficulty_layout) # Plain Text Description desc_label = QLabel("Plain Text Description:") desc_label.setFont(QFont("Arial", 10, QFont.Weight.Bold)) layout.addWidget(desc_label) self.description_text = QTextEdit() self.description_text.setFont(QFont("Arial", 10)) self.description_text.setAcceptRichText(False) layout.addWidget(self.description_text) def create_markdown_tab(self): """Create the Markdown Description tab.""" layout = QVBoxLayout(self.markdown_tab) self.description_editor = MarkdownEditor() layout.addWidget(self.description_editor) def create_test_tab(self): """Create the Test Code tab.""" layout = QVBoxLayout(self.test_tab) # Add tips label tips_label = QLabel("💡 Tips for writing good test cases will appear in the status bar") tips_label.setFont(QFont("Arial", 9)) tips_label.setStyleSheet("color: #666; padding: 5px;") layout.addWidget(tips_label) self.test_code_editor = CodeEditor() layout.addWidget(self.test_code_editor) # Insert template code self._insert_template_code() def _insert_template_code(self): """Insert template test code into the editor.""" template_code = '''import unittest class TestSolution(unittest.TestCase): def test_example_case(self): """ Test the provided example case. """ solution = Solution() result = solution.solve("input") self.assertEqual(result, "expected_output") def test_edge_case_empty_input(self): """ Test with empty input. """ solution = Solution() result = solution.solve("") self.assertEqual(result, "") def test_edge_case_large_input(self): """ Test with a large input to check performance. """ solution = Solution() large_input = "a" * 1000 result = solution.solve(large_input) self.assertTrue(result) # Adjust based on expected behavior if __name__ == "__main__": unittest.main() ''' self.test_code_editor.setPlainText(template_code) def validate_inputs(self): """Validate all form inputs.""" if not self.problem_name.text().strip(): QMessageBox.critical(self, "Error", "Problem name is required!") return False if not self.description_text.toPlainText().strip(): QMessageBox.critical(self, "Error", "Plain text description is required!") return False if not self.description_editor.toPlainText().strip(): QMessageBox.critical(self, "Error", "Markdown description is required!") return False test_code = self.test_code_editor.toPlainText().strip() if not test_code or "pass" in test_code and len(test_code) < 100: reply = QMessageBox.question( self, "Confirm", "The test code seems minimal. Are you sure you want to proceed?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.No: return False # Validate problem name (should be filesystem-safe) name = self.problem_name.text().strip() if not name.replace("_", "").replace("-", "").replace(" ", "").isalnum(): QMessageBox.critical( self, "Error", "Problem name should only contain letters, numbers, spaces, hyphens, and underscores!" ) return False return True def create_problem(self): """Create a new problem from the form data.""" if not self.validate_inputs(): return try: # Get values problem_name = self.problem_name.text().strip() description_text = self.description_text.toPlainText().strip() # Plain text description_md = self.description_editor.toPlainText().strip() # Markdown difficulty = self.difficulty.currentText() test_code = self.test_code_editor.toPlainText().strip() # Create safe folder name (replace spaces with underscores) folder_name = problem_name.replace(" ", "_").lower() # Create directory structure problem_path = self.base_path / folder_name # Create directories if they don't exist problem_path.mkdir(parents=True, exist_ok=True) # Create manifest.json - Include both description fields manifest = { "title": problem_name, "description": description_text, # Plain text description "description_md": f"problems/{folder_name}/description.md", # Markdown file path "test_code": f"problems/{folder_name}/test.py", "difficulty": difficulty } manifest_path = problem_path / "manifest.json" with open(manifest_path, 'w', encoding='utf-8') as f: json.dump(manifest, f, indent=4, ensure_ascii=False) # Create description.md description_md_path = problem_path / "description.md" with open(description_md_path, 'w', encoding='utf-8') as f: f.write(description_md) # Create test.py test_py_path = problem_path / "test.py" with open(test_py_path, 'w', encoding='utf-8') as f: f.write(test_code) self.statusBar().showMessage(f"✓ Problem '{problem_name}' created successfully in {problem_path}") logger.info(f"Created problem: {problem_name} at {problem_path}") reply = QMessageBox.question( self, "Success", f"Problem '{problem_name}' created successfully!\n\n" f"Location: {problem_path}\n\n" "Would you like to open the folder?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.open_folder(problem_path) except Exception as e: error_msg = f"Error creating problem: {str(e)}" self.statusBar().showMessage(error_msg) logger.error(error_msg) QMessageBox.critical(self, "Error", error_msg) def open_folder(self, path): """Cross-platform folder opening.""" try: if sys.platform == "win32": os.startfile(path) elif sys.platform == "darwin": # macOS os.system(f"open '{path}'") else: # Linux and other Unix-like os.system(f"xdg-open '{path}'") except Exception as e: error_msg = f"Could not open folder: {str(e)}" logger.warning(error_msg) QMessageBox.warning(self, "Warning", error_msg) def clear_all(self): """Clear all form fields.""" reply = QMessageBox.question( self, "Confirm", "Clear all fields?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.problem_name.clear() self.description_text.clear() self.description_editor.setPlainText("") self.difficulty.setCurrentText("medium") self.test_code_editor.clear() # Re-insert template self._insert_template_code() self.statusBar().showMessage("All fields cleared.") logger.info("Cleared all form fields") def load_existing(self): """Load an existing problem for editing.""" try: if not self.base_path.exists(): QMessageBox.warning(self, "Warning", "No problems directory found!") return # Get list of existing problems problems = [d.name for d in self.base_path.iterdir() if d.is_dir()] if not problems: QMessageBox.information(self, "Info", "No existing problems found!") return # Create and show selection dialog dialog = LoadProblemDialog(problems, self) if dialog.exec() == QDialog.DialogCode.Accepted: selected_problem = dialog.selected_problem() if selected_problem: self.load_problem_data(selected_problem) except Exception as e: error_msg = f"Error loading existing problems: {str(e)}" logger.error(error_msg) QMessageBox.critical(self, "Error", error_msg) def load_problem_data(self, problem_name): """Load problem data into the form.""" try: problem_path = self.base_path / problem_name manifest_path = problem_path / "manifest.json" test_path = problem_path / "test.py" desc_path = problem_path / "description.md" # Load manifest with open(manifest_path, 'r', encoding='utf-8') as f: manifest = json.load(f) # Load test code test_code = "" if test_path.exists(): with open(test_path, 'r', encoding='utf-8') as f: test_code = f.read() # Load markdown description description_md = "" if desc_path.exists(): with open(desc_path, 'r', encoding='utf-8') as f: description_md = f.read() # Load plain text description from manifest description_text = manifest.get('description', '') # Populate fields self.problem_name.setText(manifest["title"]) self.description_text.setPlainText(description_text) self.description_editor.setPlainText(description_md) self.difficulty.setCurrentText(manifest["difficulty"]) self.test_code_editor.setPlainText(test_code) self.statusBar().showMessage(f"Loaded problem: {problem_name}") logger.info(f"Loaded problem: {problem_name}") except Exception as e: error_msg = f"Error loading problem data: {str(e)}" logger.error(error_msg) QMessageBox.critical(self, "Error", error_msg) def main(): """Main application entry point.""" app = QApplication(sys.argv) # Set application style app.setStyle('Fusion') window = ProblemCreatorApp() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()