Возникло исключение: AttributeError
'NoneType' object has no attribute 'setMenuBar'
  File "C:\Users\Admin\Desktop\Casino AI\ddd.py", line 275, in create_menu
    self.layout().setMenuBar(self.menuBar)
    ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Admin\Desktop\Casino AI\ddd.py", line 128, in init_ui
    self.create_menu()
  File "C:\Users\Admin\Desktop\Casino AI\ddd.py", line 101, in __init__
    self.init_ui()
  File "C:\Users\Admin\Desktop\Casino AI\ddd.py", line 627, in main
    chatbot = ChatBot()
              ^^^^^^^^^
  File "C:\Users\Admin\Desktop\Casino AI\ddd.py", line 633, in <module>
    main()
AttributeError: 'NoneType' object has no attribute 'setMenuBar'
```python
import sys
import re
import requests
import json
import os
from PyQt5.QtWidgets import (
    QApplication, QFontComboBox, QMenuBar, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit,
    QLineEdit, QPushButton, QLabel, QFileDialog, QMessageBox,
    QDialog, QScrollArea, QFrame, QAction, QMenu, QInputDialog, QColorDialog, QFontDialog, QComboBox
)
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QThread
from PyQt5.QtGui import QFont, QIcon, QTextCursor, QColor
from gradio_client import Client
import markdown
from pygments import highlight
from pygments.lexers import PythonLexer, JavascriptLexer, JsonLexer, TextLexer
from pygments.formatters import HtmlFormatter
from PyQt5.QtPrintSupport import QPrinter

# Инициализация клиента для API
client = Client("yuntian-deng/o1mini")

# Карта поддерживаемых лексеров для подсветки синтаксиса
LEXER_MAP = {
    'python': PythonLexer(),
    'javascript': JavascriptLexer(),
    'json': JsonLexer(),
    # Добавьте другие языки здесь
}

def highlight_code(text, language='python'):
    """
    Подсвечивает синтаксис кода.
    Поддерживаемые языки можно расширять, добавляя их в LEXER_MAP.
    """
    lexer = LEXER_MAP.get(language.lower(), TextLexer())

    formatter = HtmlFormatter(style="friendly")
    highlighted = highlight(text, lexer, formatter)
    return highlighted

class Worker(QObject):
    """
    Рабочий объект для выполнения API-запросов в отдельном потоке.
    """
    finished = pyqtSignal(str)
    error = pyqtSignal(str)

    def __init__(self, message, chat_history, chat_counter):
        super().__init__()
        self.message = message
        self.chat_history = chat_history
        self.chat_counter = chat_counter

    def run(self):
        try:
            result = client.predict(
                inputs=self.message,
                top_p=1,
                temperature=1,
                chat_counter=self.chat_counter,
                chatbot=self.chat_history,
                api_name="/predict"
            )

            if len(result) < 4:
                raise ValueError("Некорректный формат ответа от API.")

            chatbot_history, new_chat_counter, status_code, reset_text = result

            if not isinstance(chatbot_history, (list, tuple)):
                raise TypeError("chatbot_history должно быть списком или кортежем.")

            if len(chatbot_history) == 0:
                chatbot_response = "Нет ответа от бота."
            else:
                latest_entry = chatbot_history[-1]
                if isinstance(latest_entry, (list, tuple)) and len(latest_entry) == 2:
                    chatbot_response = latest_entry[1]
                else:
                    chatbot_response = "Не удалось получить ответ бота."

            self.finished.emit(chatbot_response)

        except Exception as e:
            self.error.emit(str(e))


class ChatBot(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Чат-бот на основе API `yuntian-deng/o1mini`")
        self.setGeometry(100, 100, 900, 700)

        self.chat_history = []
        self.chat_counter = 0

        # Загрузка настроек
        self.load_settings()

        self.init_ui()

    def load_settings(self):
        """
        Загружает настройки из файла конфигурации.
        """
        self.config_path = 'config.json'
        if os.path.exists(self.config_path):
            try:
                with open(self.config_path, 'r', encoding='utf-8') as f:
                    self.settings = json.load(f)
            except:
                self.settings = {}
        else:
            self.settings = {}

    def save_settings(self):
        """
        Сохраняет текущие настройки в файл конфигурации.
        """
        with open(self.config_path, 'w', encoding='utf-8') as f:
            json.dump(self.settings, f, ensure_ascii=False, indent=4)

    def init_ui(self):
        main_layout = QVBoxLayout()

        # Меню
        self.create_menu()

        # Заголовок
        title = QLabel(self.tr("Чат-бот на основе API `yuntian-deng/o1mini`"))
        title.setFont(QFont("Arial", 20))
        title.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(title)

        # Область чата с прокруткой
        self.chat_area = QTextEdit()
        self.chat_area.setReadOnly(True)
        self.chat_area.setFont(QFont("Arial", 12))
        # Применение сохранённого цвета фона, если есть
        bg_color = self.settings.get('chat_bg_color', '#f9f9f9')
        self.chat_area.setStyleSheet(f"""
            QTextEdit {{
                background-color: {bg_color};
                border: 1px solid #ccc;
                border-radius: 5px;
                padding: 10px;
            }}
        """)

        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        scroll.setWidget(self.chat_area)
        scroll.setFrameStyle(QFrame.NoFrame)
        main_layout.addWidget(scroll)

        # Поле ввода сообщения и подсчёт символов и токенов
        input_container = QWidget()
        input_layout = QVBoxLayout()

        self.input_field = QTextEdit()
        self.input_field.setPlaceholderText(self.tr("Введите сообщение и нажмите Enter"))
        self.input_field.setFixedHeight(100)
        self.input_field.setFont(QFont("Arial", 12))
        self.input_field.setStyleSheet("""
            QTextEdit {
                padding: 8px;
                border: 1px solid #ccc;
                border-radius: 5px;
            }
        """)
        self.input_field.installEventFilter(self)
        input_layout.addWidget(self.input_field)

        counters_layout = QHBoxLayout()
        self.char_count_label = QLabel(self.tr("Символы: 0"))
        self.token_count_label = QLabel(self.tr("Токены: 0"))
        counters_layout.addWidget(self.char_count_label)
        counters_layout.addWidget(self.token_count_label)
        counters_layout.addStretch()
        input_layout.addLayout(counters_layout)

        input_container.setLayout(input_layout)
        main_layout.addWidget(input_container)

        # Кнопка отправки и индикатор статуса
        send_layout = QHBoxLayout()
        self.send_button = QPushButton(self.tr("Отправить"))
        self.send_button.setIcon(QIcon.fromTheme("send"))
        self.send_button.clicked.connect(self.send_message)
        send_layout.addWidget(self.send_button)

        self.status_label = QLabel("")
        self.status_label.setStyleSheet("color: green;")
        send_layout.addWidget(self.status_label)

        main_layout.addLayout(send_layout)

        # Кнопки дополнительных функций
        func_layout = QHBoxLayout()

        self.reset_btn = QPushButton(self.tr("Сбросить чат"))
        self.reset_btn.setIcon(QIcon.fromTheme("edit-clear"))
        self.reset_btn.clicked.connect(self.reset_chat)
        func_layout.addWidget(self.reset_btn)

        self.export_btn = QPushButton(self.tr("Экспортировать чат"))
        self.export_btn.setIcon(QIcon.fromTheme("document-save"))
        self.export_btn.clicked.connect(self.export_chat)
        func_layout.addWidget(self.export_btn)

        self.pdf_export_btn = QPushButton(self.tr("Экспортировать в PDF"))
        self.pdf_export_btn.setIcon(QIcon.fromTheme("document-print"))
        self.pdf_export_btn.clicked.connect(self.export_chat_to_pdf)
        func_layout.addWidget(self.pdf_export_btn)

        self.fact_btn = QPushButton(self.tr("Случайный факт"))
        self.fact_btn.setIcon(QIcon.fromTheme("information"))
        self.fact_btn.clicked.connect(self.get_random_fact)
        func_layout.addWidget(self.fact_btn)

        main_layout.addLayout(func_layout)

        # Информация о ошибках
        self.info_label = QLabel("")
        self.info_label.setStyleSheet("color: red;")
        main_layout.addWidget(self.info_label)

        # Применение стилей
        self.setStyleSheet("""
            QWidget {
                background-color: #e6e6e6;
            }
            QPushButton {
                padding: 8px 16px;
                border: none;
                background-color: #4CAF50;
                color: white;
                border-radius: 5px;
            }
            QPushButton:hover {
                background-color: #45a049;
            }
            QLabel {
                font-size: 12px;
            }
        """)

        self.setLayout(main_layout)

    def create_menu(self):
        """
        Создаёт меню для настроек и локализации.
        """
        menu_bar = QMenu()
        
        # Настройки
        settings_menu = QMenu(self.tr('Настройки'), self)
        settings_action = QAction(self.tr("Настройки"), self)
        settings_action.triggered.connect(self.open_settings)
        settings_menu.addAction(settings_action)
        menu_bar.addMenu(settings_menu)
        
        # Язык интерфейса
        language_menu = QMenu(self.tr('Язык'), self)
        self.language_action = QAction(self.tr("Сменить язык"), self)
        self.language_action.triggered.connect(self.change_language)
        language_menu.addAction(self.language_action)
        menu_bar.addMenu(language_menu)
        
        # Добавление меню в верхнюю часть окна
        self.menuBar = QMenuBar(self)
        self.menuBar.addMenu(settings_menu)
        self.menuBar.addMenu(language_menu)
        self.layout().setMenuBar(self.menuBar)

    def change_language(self):
        """
        Меняет язык интерфейса.
        """
        languages = {
            'Русский': 'ru',
            'English': 'en'
        }
        lang, ok = QInputDialog.getItem(self, self.tr("Выберите язык"), self.tr("Язык:"), list(languages.keys()), 0, False)
        if ok and lang:
            self.current_language = languages[lang]
            self.settings['language'] = self.current_language
            self.save_settings()
            QMessageBox.information(self, self.tr("Перезагрузка"), self.tr("Изменения вступят в силу после перезагрузки приложения."))
            # Для полной смены языка необходимо перезапустить приложение
            # Здесь можно реализовать автоматический перезапуск или перезапись интерфейса

    def append_message(self, sender, message):
        """
        Добавляет сообщение в чат. Обрабатывает текст и блоки кода.
        """
        # Регулярное выражение для поиска блоков кода
        code_pattern = re.compile(r'```(\w+)?\n(.*?)```', re.DOTALL)
        parts = code_pattern.split(message)

        # Проход по частям сообщения
        for i in range(0, len(parts), 3):
            text = parts[i].strip()
            if text:
                formatted_text = self.format_text(sender, text)
                self.chat_area.append(formatted_text)

            # Проверяем наличие блока кода
            if i + 2 < len(parts):
                language = parts[i + 1] if parts[i + 1] else 'python'
                code = parts[i + 2].strip()
                highlighted_code = highlight_code(code, language)
                formatted_code = f"""
                <div style="background-color:#f4f4f4; padding:10px; border-radius:5px; position:relative;">
                    {highlighted_code}
                    <button onclick="copyToClipboard(this)" style="position:absolute; top:5px; right:5px; padding:2px 5px; font-size:10px;">{self.tr('Копировать')}</button>
                </div>
                <script>
                function copyToClipboard(button) {{
                    var code = button.parentElement.querySelector('pre').innerText;
                    navigator.clipboard.writeText(code).then(function() {{
                        button.textContent = '{self.tr('Скопировано!')}';
                        setTimeout(function() {{
                            button.textContent = '{self.tr('Копировать')}';
                        }}, 2000);
                    }}, function(err) {{
                        alert('{self.tr('Не удалось скопировать код')}');
                    }});
                }}
                </script>
                """
                self.chat_area.append(formatted_code)
                # TODO: Реализовать интерактивное выполнение кода или расширенные возможности редактирования

        self.chat_area.moveCursor(QTextCursor.End)

    def format_text(self, sender, text):
        """
        Форматирует текстовое сообщение.
        """
        if sender == self.tr("Вы"):
            align = 'right'
            color = '#0084ff'  # Синий для пользователя
        else:
            align = 'left'
            color = '#333333'  # Тёмно-серый для бота

        formatted = f"<p style='text-align: {align};'><span style='color: {color};'><b>{sender}:</b> {self.escape_html(text)}</span></p>"
        return formatted

    def escape_html(self, text):
        """
        Экранирует специальные HTML символы для предотвращения XSS атак.
        """
        import html
        return html.escape(text)

    def update_counters(self):
        """
        Обновляет подсчёт символов и токенов.
        """
        text = self.input_field.toPlainText()
        char_count = len(text)
        token_count = char_count // 4  # 1 токен = 4 символа
        self.char_count_label.setText(self.tr(f"Символы: {char_count}"))
        self.token_count_label.setText(self.tr(f"Токены: {token_count}"))

    def send_message(self):
        """
        Обрабатывает отправку сообщения.
        """
        user_message = self.input_field.toPlainText().strip()
        if not user_message:
            return

        self.append_message(self.tr("Вы"), user_message)
        self.chat_history.append((user_message, ""))
        self.input_field.clear()
        self.update_counters()

        # Показываем статус "Бот набирает..."
        self.status_label.setText(self.tr("Бот набирает ответ..."))
        QApplication.processEvents()

        # Запускаем рабочий поток для асинхронного запроса
        self.thread = QThread()
        self.worker = Worker(user_message, self.chat_history, self.chat_counter)
        self.worker.moveToThread(self.thread)
        self.thread.started.connect(self.worker.run)
        self.worker.finished.connect(self.handle_response)
        self.worker.error.connect(self.handle_error)
        self.worker.finished.connect(self.thread.quit)
        self.worker.finished.connect(self.worker.deleteLater)
        self.thread.finished.connect(self.thread.deleteLater)
        self.thread.start()

    def handle_response(self, response):
        """
        Обрабатывает ответ от бота.
        """
        self.status_label.setText("")
        self.chat_counter += 1
        self.chat_history[-1] = (self.chat_history[-1][0], response)
        self.append_message(self.tr("Бот"), response)

    def handle_error(self, error_message):
        """
        Обрабатывает ошибку при запросе.
        """
        self.status_label.setText("")
        QMessageBox.critical(self, self.tr("Ошибка"), self.tr(f"Не удалось отправить сообщение:\n{error_message}"))

    def reset_chat(self):
        """
        Сбрасывает историю чата.
        """
        confirmation = QMessageBox.question(
            self, self.tr("Сбросить чат"),
            self.tr("Вы уверены, что хотите сбросить историю чата?"),
            QMessageBox.Yes | QMessageBox.No
        )
        if confirmation == QMessageBox.Yes:
            self.chat_history = []
            self.chat_counter = 0
            self.chat_area.clear()
            QMessageBox.information(self, self.tr("Чат сброшен"), self.tr("История чата была сброшена."))

    def export_chat(self):
        """
        Экспортирует историю чата в файл HTML или TXT.
        """
        if not self.chat_history:
            QMessageBox.warning(self, self.tr("Экспорт"), self.tr("Нет истории чата для экспорта."))
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, self.tr("Сохранить чат"), "", 
            self.tr("HTML файлы (*.html);;Текстовые файлы (*.txt)"), options=options
        )
        if file_path:
            try:
                if file_path.endswith('.html'):
                    with open(file_path, "w", encoding="utf-8") as file:
                        file.write("<html><head><meta charset='UTF-8'><title>Чат</title></head><body>")
                        for user_msg, bot_msg in self.chat_history:
                            file.write(f"<p style='text-align: right;'><b>{self.tr('Вы')}:</b> {self.escape_html(user_msg)}</p>")
                            # Обработка возможных блоков кода в сообщении бота
                            code_pattern = re.compile(r'```(\w+)?\n(.*?)```', re.DOTALL)
                            parts = code_pattern.split(bot_msg)
                            for i in range(0, len(parts), 3):
                                text = parts[i].strip()
                                if text:
                                    file.write(f"<p style='text-align: left;'><b>{self.tr('Бот')}:</b> {self.escape_html(text)}</p>")
                                if i + 2 < len(parts):
                                    language = parts[i + 1] if parts[i + 1] else 'python'
                                    code = parts[i + 2].strip()
                                    highlighted_code = highlight_code(code, language)
                                    file.write(f"<pre style='background-color:#f4f4f4; padding:10px; border-radius:5px;'>{highlighted_code}</pre>")
                        file.write("</body></html>")
                else:
                    with open(file_path, "w", encoding="utf-8") as file:
                        for user_msg, bot_msg in self.chat_history:
                            file.write(f"{self.tr('Вы')}: {user_msg}\n{self.tr('Бот')}: {bot_msg}\n\n")
                QMessageBox.information(self, self.tr("Экспорт"), self.tr(f"Чат успешно сохранен в {file_path}"))
            except Exception as e:
                QMessageBox.critical(self, self.tr("Ошибка"), self.tr(f"Не удалось сохранить чат:\n{e}"))

    def export_chat_to_pdf(self):
        """
        Экспортирует историю чата в PDF с сохранением форматирования и подсветки кода.
        """
        if not self.chat_history:
            QMessageBox.warning(self, self.tr("Экспорт PDF"), self.tr("Нет истории чата для экспорта."))
            return

        options = QFileDialog.Options()
        file_path, _ = QFileDialog.getSaveFileName(
            self, self.tr("Сохранить чат в PDF"), "", 
            self.tr("PDF файлы (*.pdf)"), options=options
        )
        if file_path:
            try:
                printer = QPrinter(QPrinter.HighResolution)
                printer.setOutputFormat(QPrinter.PdfFormat)
                printer.setOutputFileName(file_path)
                document = self.chat_area.document()
                document.print_(printer)
                QMessageBox.information(self, self.tr("Экспорт PDF"), self.tr(f"Чат успешно сохранен в {file_path}"))
            except Exception as e:
                QMessageBox.critical(self, self.tr("Ошибка"), self.tr(f"Не удалось сохранить PDF:\n{e}"))

    def get_random_fact(self):
        """
        Получает случайный факт и добавляет его в чат.
        """
        try:
            response = requests.get("https://uselessfacts.jsph.pl/random.json?language=ru", timeout=5)
            if response.status_code == 200:
                fact = response.json().get("text", self.tr("Не удалось получить факт."))
            else:
                fact = self.tr("Ошибка при получении факта.")
        except Exception as e:
            fact = self.tr(f"Ошибка при получении факта: {e}")

        self.append_message(self.tr("Бот"), fact)

    def eventFilter(self, obj, event):
        """
        Обрабатывает события для поля ввода сообщения.
        Позволяет использовать Shift + Enter для вставки новой строки.
        """
        if obj == self.input_field:
            if event.type() == event.KeyPress:
                if event.key() in (Qt.Key_Return, Qt.Key_Enter):
                    if event.modifiers() == Qt.ShiftModifier:
                        cursor = self.input_field.textCursor()
                        cursor.insertBlock()
                        return True
                    else:
                        self.send_message()
                        return True
        return super().eventFilter(obj, event)

    def open_settings(self):
        """
        Открывает диалог настроек.
        """
        settings_dialog = SettingsDialog(self)
        if settings_dialog.exec_():
            # Применение сохранённых настроек
            bg_color = self.settings.get('chat_bg_color', '#f9f9f9')
            self.chat_area.setStyleSheet(f"""
                QTextEdit {{
                    background-color: {bg_color};
                    border: 1px solid #ccc;
                    border-radius: 5px;
                    padding: 10px;
                }}
            """)
            font = self.settings.get('chat_font', "Arial")
            font_size = self.settings.get('chat_font_size', 12)
            self.chat_area.setFont(QFont(font, font_size))
            self.save_settings()

class SettingsDialog(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setWindowTitle(self.tr("Настройки"))
        self.setFixedSize(400, 300)
        self.parent = parent
        self.init_ui()

    def init_ui(self):
        layout = QVBoxLayout()

        # Выбор цвета чата
        color_btn = QPushButton(self.tr("Выбрать цвет фона чата"))
        color_btn.clicked.connect(self.choose_color)
        layout.addWidget(color_btn)

        # Выбор шрифта
        font_btn = QPushButton(self.tr("Выбрать шрифт чата"))
        font_btn.clicked.connect(self.choose_font)
        layout.addWidget(font_btn)

        # Дополнительные настройки
        font_label = QLabel(self.tr("Шрифт:"))
        layout.addWidget(font_label)

        self.font_combo = QFontComboBox()
        current_font = self.parent.settings.get('chat_font', "Arial")
        self.font_combo.setCurrentFont(QFont(current_font))
        layout.addWidget(self.font_combo)

        size_label = QLabel(self.tr("Размер шрифта:"))
        layout.addWidget(size_label)

        self.size_combo = QComboBox()
        self.size_combo.addItems([str(size) for size in range(8, 30, 2)])
        current_size = str(self.parent.settings.get('chat_font_size', 12))
        index = self.size_combo.findText(current_size)
        if index >= 0:
            self.size_combo.setCurrentIndex(index)
        layout.addWidget(self.size_combo)

        # Кнопки OK и Отмена
        btn_layout = QHBoxLayout()
        ok_btn = QPushButton(self.tr("OK"))
        ok_btn.clicked.connect(self.accept)
        cancel_btn = QPushButton(self.tr("Отмена"))
        cancel_btn.clicked.connect(self.reject)
        btn_layout.addWidget(ok_btn)
        btn_layout.addWidget(cancel_btn)
        layout.addLayout(btn_layout)

        self.setLayout(layout)

    def choose_color(self):
        color = QColorDialog.getColor()
        if color.isValid():
            self.parent.settings['chat_bg_color'] = color.name()

    def choose_font(self):
        font, ok = QFontDialog.getFont()
        if ok:
            self.parent.settings['chat_font'] = font.family()
            self.parent.settings['chat_font_size'] = font.pointSize()

    def accept(self):
        # Сохранение выбранных шрифта и размера
        selected_font = self.font_combo.currentFont().family()
        selected_size = int(self.size_combo.currentText())
        self.parent.settings['chat_font'] = selected_font
        self.parent.settings['chat_font_size'] = selected_size
        super().accept()


def main():
    app = QApplication(sys.argv)
    
    # Установка переводов (для полной локализации необходимо добавить файлы перевода)
    # Ниже приведен пример реализации, однако для полноценной поддержки языков потребуется использование Qt Linguist
    # и создание соответствующих `.qm` файлов.

    chatbot = ChatBot()
    chatbot.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
```