import sys import json import os from datetime import datetime from PyQt6.QtCore import * from PyQt6.QtWidgets import * from PyQt6.QtWebEngineWidgets import * from PyQt6.QtWebEngineCore import * from PyQt6.QtGui import * class BrowserWindow(QMainWindow): def __init__(self): super().__init__() # Настройки поисковых систем self.search_engines = { "Google": "https://www.google.com/search?q={}", "DuckDuckGo": "https://duckduckgo.com/?q={}", "Bing": "https://www.bing.com/search?q={}", "Yandex": "https://yandex.ru/search/?text={}" } self.current_search_engine = "Google" # Инициализация данных self.bookmarks = [] self.history = [] self.load_data() self.setup_ui() self.apply_dark_blue_theme() def setup_ui(self): """Настройка пользовательского интерфейса""" self.setWindowTitle("Dark Blue Browser") self.setGeometry(100, 100, 1400, 800) # Центральный виджет central_widget = QWidget() self.setCentralWidget(central_widget) # Основной макет main_layout = QVBoxLayout(central_widget) # Панель навигации nav_bar = QHBoxLayout() # Кнопка назад self.back_btn = QPushButton("←") self.back_btn.clicked.connect(self.navigate_back) # Кнопка вперед self.forward_btn = QPushButton("→") self.forward_btn.clicked.connect(self.navigate_forward) # Кнопка обновить self.reload_btn = QPushButton("↻") self.reload_btn.clicked.connect(self.reload_page) # Адресная строка self.url_bar = QLineEdit() self.url_bar.returnPressed.connect(self.navigate_to_url) # Кнопка перехода self.go_btn = QPushButton("Go") self.go_btn.clicked.connect(self.navigate_to_url) # Выбор поисковой системы self.search_combo = QComboBox() self.search_combo.addItems(self.search_engines.keys()) self.search_combo.setCurrentText(self.current_search_engine) self.search_combo.currentTextChanged.connect(self.change_search_engine) # Поле поиска self.search_bar = QLineEdit() self.search_bar.setPlaceholderText("Поиск...") self.search_bar.returnPressed.connect(self.perform_search) # Кнопка закладок self.bookmark_btn = QPushButton("⭐") self.bookmark_btn.clicked.connect(self.toggle_bookmark) self.bookmark_btn.setToolTip("Добавить/удалить закладку") # Кнопка истории self.history_btn = QPushButton("📜") self.history_btn.clicked.connect(self.show_history) self.history_btn.setToolTip("Показать историю") # Добавление элементов в панель навигации nav_bar.addWidget(self.back_btn) nav_bar.addWidget(self.forward_btn) nav_bar.addWidget(self.reload_btn) nav_bar.addWidget(QLabel("URL:")) nav_bar.addWidget(self.url_bar, 2) nav_bar.addWidget(self.go_btn) nav_bar.addWidget(QLabel("Поиск:")) nav_bar.addWidget(self.search_bar, 1) nav_bar.addWidget(self.search_combo) nav_bar.addWidget(self.bookmark_btn) nav_bar.addWidget(self.history_btn) # Веб-вью self.browser = QWebEngineView() self.browser.urlChanged.connect(self.update_url) self.browser.loadFinished.connect(self.on_page_loaded) # Принудительное включение темного режима для всех сайтов profile = QWebEngineProfile.defaultProfile() profile.setHttpUserAgent(profile.httpUserAgent() + " DarkMode/1.0") # Добавление в макет main_layout.addLayout(nav_bar) main_layout.addWidget(self.browser) # Загрузка домашней страницы self.load_home_page() def apply_dark_blue_theme(self): """Применение темно-голубой темы""" dark_blue_theme = """ QMainWindow { background-color: #0f1c2e; } QWidget { background-color: #0f1c2e; color: #e0f0ff; font-family: 'Segoe UI', Arial, sans-serif; font-size: 12px; } QPushButton { background-color: #1e3a5f; color: #e0f0ff; border: 1px solid #2d4a7a; border-radius: 4px; padding: 5px 12px; font-weight: bold; } QPushButton:hover { background-color: #2d4a7a; border-color: #3d5a9a; } QPushButton:pressed { background-color: #1a3257; } QLineEdit { background-color: #1a2b44; color: #ffffff; border: 1px solid #2d4a7a; border-radius: 4px; padding: 5px; selection-background-color: #2d4a7a; } QLineEdit:focus { border: 1px solid #4a7ac9; } QComboBox { background-color: #1a2b44; color: #ffffff; border: 1px solid #2d4a7a; border-radius: 4px; padding: 5px; min-width: 100px; } QComboBox::drop-down { border: none; } QComboBox QAbstractItemView { background-color: #1a2b44; color: #ffffff; selection-background-color: #2d4a7a; } QLabel { color: #a0c8ff; font-weight: bold; } QWebEngineView { background-color: #0a1525; border: 1px solid #1e3a5f; border-radius: 4px; } QMenu { background-color: #1a2b44; color: #ffffff; border: 1px solid #2d4a7a; } QMenu::item:selected { background-color: #2d4a7a; } """ self.setStyleSheet(dark_blue_theme) # Также применяем темную тему для веб-контента settings = self.browser.settings() settings.setAttribute(QWebEngineSettings.WebAttribute.JavascriptEnabled, True) def load_home_page(self): """Загрузка домашней страницы""" self.browser.setUrl(QUrl("https://www.google.com")) def navigate_to_url(self): """Переход по URL""" url = self.url_bar.text() # Проверка, является ли ввод поисковым запросом if not url.startswith(('http://', 'https://')): if ' ' in url or '.' not in url: # Это поисковый запрос self.perform_search_with_query(url) return else: url = 'https://' + url self.browser.setUrl(QUrl(url)) def perform_search(self): """Выполнение поиска""" query = self.search_bar.text() if query: self.perform_search_with_query(query) def perform_search_with_query(self, query): """Выполнение поиска с заданным запросом""" search_url = self.search_engines[self.current_search_engine].format(query) self.browser.setUrl(QUrl(search_url)) # Добавление в историю поиска self.add_to_history(query, search_url) def change_search_engine(self, engine): """Изменение поисковой системы""" self.current_search_engine = engine def navigate_back(self): """Навигация назад""" self.browser.back() def navigate_forward(self): """Навигация вперед""" self.browser.forward() def reload_page(self): """Обновление страницы""" self.browser.reload() def update_url(self, q): """Обновление URL в адресной строке""" self.url_bar.setText(q.toString()) # Добавление в историю посещений if q.toString() not in [h['url'] for h in self.history[-20:]]: self.add_to_history(self.browser.page().title(), q.toString()) def on_page_loaded(self, ok): """Действия после загрузки страницы""" if ok: current_url = self.browser.url().toString() self.url_bar.setText(current_url) # Применение темного режима к странице через JavaScript dark_mode_js = """ // Создаем стиль для темного режима let darkModeStyle = document.createElement('style'); darkModeStyle.id = 'dark-mode-style'; darkModeStyle.textContent = ` body { background-color: #0a1525 !important; color: #e0f0ff !important; } * { background-color: rgba(10, 21, 37, 0.9) !important; color: #e0f0ff !important; border-color: #2d4a7a !important; } input, textarea, select { background-color: #1a2b44 !important; color: #ffffff !important; } a { color: #4a7ac9 !important; } a:visited { color: #8a5ac9 !important; } button, .btn { background-color: #1e3a5f !important; color: #e0f0ff !important; border-color: #2d4a7a !important; } `; // Удаляем старый стиль если есть let oldStyle = document.getElementById('dark-mode-style'); if (oldStyle) oldStyle.remove(); // Добавляем новый стиль document.head.appendChild(darkModeStyle); // Дополнительные настройки для популярных сайтов if (window.location.hostname.includes('google.com')) { // Для Google document.body.style.backgroundColor = '#0a1525'; document.body.style.color = '#e0f0ff'; } if (window.location.hostname.includes('youtube.com')) { // Для YouTube document.body.style.backgroundColor = '#0a1525'; } """ # Внедряем JavaScript для темного режима self.browser.page().runJavaScript(dark_mode_js) def toggle_bookmark(self): """Добавление/удаление закладки""" current_url = self.browser.url().toString() page_title = self.browser.page().title() # Проверяем, есть ли уже в закладках existing_index = -1 for i, bookmark in enumerate(self.bookmarks): if bookmark['url'] == current_url: existing_index = i break if existing_index >= 0: # Удаляем закладку del self.bookmarks[existing_index] QMessageBox.information(self, "Закладка", "Закладка удалена!") else: # Добавляем закладку self.bookmarks.append({ 'title': page_title, 'url': current_url, 'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S") }) QMessageBox.information(self, "Закладка", "Страница добавлена в закладки!") self.save_data() def show_history(self): """Показ истории поиска""" history_dialog = QDialog(self) history_dialog.setWindowTitle("История просмотров") history_dialog.setGeometry(200, 200, 600, 400) layout = QVBoxLayout() # Таблица истории table = QTableWidget() table.setColumnCount(3) table.setHorizontalHeaderLabels(["Дата", "Заголовок", "URL"]) table.setRowCount(len(self.history)) for i, item in enumerate(reversed(self.history[-50:])): # Последние 50 записей table.setItem(i, 0, QTableWidgetItem(item['date'])) table.setItem(i, 1, QTableWidgetItem(item['title'][:50] + "..." if len(item['title']) > 50 else item['title'])) table.setItem(i, 2, QTableWidgetItem(item['url'][:100] + "..." if len(item['url']) > 100 else item['url'])) table.resizeColumnsToContents() # Кнопки button_box = QDialogButtonBox() clear_btn = QPushButton("Очистить историю") close_btn = QPushButton("Закрыть") clear_btn.clicked.connect(lambda: self.clear_history(table)) close_btn.clicked.connect(history_dialog.close) button_box.addButton(clear_btn, QDialogButtonBox.ButtonRole.ActionRole) button_box.addButton(close_btn, QDialogButtonBox.ButtonRole.RejectRole) layout.addWidget(table) layout.addWidget(button_box) history_dialog.setLayout(layout) history_dialog.exec() def add_to_history(self, title, url): """Добавление записи в историю""" self.history.append({ 'title': title, 'url': url, 'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S") }) self.save_data() def clear_history(self, table): """Очистка истории""" reply = QMessageBox.question(self, "Очистка истории", "Вы уверены, что хотите очистить всю историю?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No) if reply == QMessageBox.StandardButton.Yes: self.history.clear() table.setRowCount(0) self.save_data() def save_data(self): """Сохранение данных в файлы""" data_dir = "browser_data" if not os.path.exists(data_dir): os.makedirs(data_dir) with open(os.path.join(data_dir, "bookmarks.json"), "w", encoding="utf-8") as f: json.dump(self.bookmarks, f, ensure_ascii=False, indent=2) with open(os.path.join(data_dir, "history.json"), "w", encoding="utf-8") as f: json.dump(self.history, f, ensure_ascii=False, indent=2) def load_data(self): """Загрузка данных из файлов""" data_dir = "browser_data" # Загрузка закладок bookmarks_file = os.path.join(data_dir, "bookmarks.json") if os.path.exists(bookmarks_file): with open(bookmarks_file, "r", encoding="utf-8") as f: self.bookmarks = json.load(f) # Загрузка истории history_file = os.path.join(data_dir, "history.json") if os.path.exists(history_file): with open(history_file, "r", encoding="utf-8") as f: self.history = json.load(f) def contextMenuEvent(self, event): """Контекстное меню""" menu = QMenu(self) # Добавление действий back_action = menu.addAction("Назад") forward_action = menu.addAction("Вперед") reload_action = menu.addAction("Обновить") menu.addSeparator() bookmark_action = menu.addAction("Добавить в закладки") show_bookmarks_action = menu.addAction("Показать закладки") menu.addSeparator() history_action = menu.addAction("Показать историю") settings_action = menu.addAction("Настройки") # Обработка выбора action = menu.exec(self.mapToGlobal(event.pos())) if action == back_action: self.navigate_back() elif action == forward_action: self.navigate_forward() elif action == reload_action: self.reload_page() elif action == bookmark_action: self.toggle_bookmark() elif action == show_bookmarks_action: self.show_bookmarks() elif action == history_action: self.show_history() elif action == settings_action: self.show_settings() def show_bookmarks(self): """Показ закладок""" bookmarks_dialog = QDialog(self) bookmarks_dialog.setWindowTitle("Закладки") bookmarks_dialog.setGeometry(200, 200, 600, 400) layout = QVBoxLayout() # Список закладок list_widget = QListWidget() for bookmark in self.bookmarks: item_text = f"{bookmark['title']} - {bookmark['date']}" item = QListWidgetItem(item_text) item.setData(Qt.ItemDataRole.UserRole, bookmark['url']) list_widget.addItem(item) list_widget.itemDoubleClicked.connect( lambda item: self.browser.setUrl(QUrl(item.data(Qt.ItemDataRole.UserRole))) ) # Кнопки button_box = QDialogButtonBox() open_btn = QPushButton("Открыть") delete_btn = QPushButton("Удалить") close_btn = QPushButton("Закрыть") open_btn.clicked.connect( lambda: self.open_bookmark(list_widget.currentItem()) ) delete_btn.clicked.connect( lambda: self.delete_bookmark(list_widget) ) close_btn.clicked.connect(bookmarks_dialog.close) button_box.addButton(open_btn, QDialogButtonBox.ButtonRole.ActionRole) button_box.addButton(delete_btn, QDialogButtonBox.ButtonRole.ActionRole) button_box.addButton(close_btn, QDialogButtonBox.ButtonRole.RejectRole) layout.addWidget(list_widget) layout.addWidget(button_box) bookmarks_dialog.setLayout(layout) bookmarks_dialog.exec() def open_bookmark(self, item): """Открытие закладки""" if item: url = item.data(Qt.ItemDataRole.UserRole) self.browser.setUrl(QUrl(url)) def delete_bookmark(self, list_widget): """Удаление закладки""" current_item = list_widget.currentItem() if current_item: current_row = list_widget.row(current_item) if 0 <= current_row < len(self.bookmarks): del self.bookmarks[current_row] list_widget.takeItem(current_row) self.save_data() def show_settings(self): """Показ настроек""" settings_dialog = QDialog(self) settings_dialog.setWindowTitle("Настройки браузера") settings_dialog.setGeometry(300, 300, 400, 300) layout = QVBoxLayout() # Настройки поиска search_group = QGroupBox("Настройки поиска") search_layout = QVBoxLayout() search_label = QLabel("Поисковая система по умолчанию:") self.settings_search_combo = QComboBox() self.settings_search_combo.addItems(self.search_engines.keys()) self.settings_search_combo.setCurrentText(self.current_search_engine) search_layout.addWidget(search_label) search_layout.addWidget(self.settings_search_combo) search_group.setLayout(search_layout) # Кнопки button_box = QDialogButtonBox() save_btn = QPushButton("Сохранить") cancel_btn = QPushButton("Отмена") save_btn.clicked.connect( lambda: self.save_settings(settings_dialog) ) cancel_btn.clicked.connect(settings_dialog.close) button_box.addButton(save_btn, QDialogButtonBox.ButtonRole.AcceptRole) button_box.addButton(cancel_btn, QDialogButtonBox.ButtonRole.RejectRole) layout.addWidget(search_group) layout.addWidget(button_box) settings_dialog.setLayout(layout) settings_dialog.exec() def save_settings(self, dialog): """Сохранение настроек""" self.current_search_engine = self.settings_search_combo.currentText() self.search_combo.setCurrentText(self.current_search_engine) dialog.close() QMessageBox.information(self, "Настройки", "Настройки сохранены!") def main(): app = QApplication(sys.argv) app.setApplicationName("Dark Blue Browser") app.setStyle("Fusion") window = BrowserWindow() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()