浏览器3.8

一个使用Python和PyQt5框架实现的网页浏览器的框架。这个浏览器包含以下主要特性:
1自定义WebEngineView:这个类是QWebEngineView的子类,它添加了两个信号:pageLoaded表示网页加载完成,openLinkInNewTabSignal表示当用户点击链接时,是否应该在新标签页中打开链接。
2MainWindow:这是浏览器窗口的主要类,它包含一个QTabWidget,用于管理多个标签页;一个工具栏,包含后退、前进、刷新等按钮;一个地址栏,用户可以输入URL;一个显示框,用于显示当前网站的标题;一个搜索栏,用户可以搜索网站;以及一个列表控件,用于显示浏览历史。
3导航和历史记录:用户可以通过点击标签页来切换不同的标签页。点击历史列表中的项,可以回到该URL对应的页面。用户可以通过点击工具栏上的后退、前进、刷新和停止按钮来导航。
4导入HTML文件:用户可以通过点击“导入HTML文件”菜单选项来选择一个HTML文件,然后在新标签页中打开它。
日志记录:应用程序使用logging模块记录日志,以便于调试和记录应用的行为。

import logging
from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QPushButton, QVBoxLayout, QWidget, QMenuBar, QMenu, QAction, QFileDialog, QMessageBox, QTabWidget, QTabBar, QHBoxLayout, QToolBar, QLabel, QListWidget
from PyQt5.QtCore import QUrl, pyqtSignal, QObject, Qt
from PyQt5.QtGui import QMouseEvent  # 更正导入语句
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage
import os

# 配置日志记录器
logging.basicConfig(filename='app.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

class CustomWebEngineView(QWebEngineView):
    pageLoaded = pyqtSignal(bool)  # 添加一个信号来表示网页加载状态
    openLinkInNewTabSignal = pyqtSignal(QUrl)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.page().linkHovered.connect(self.linkHovered)
        self.hoveredLink = ""


    def contextMenuEvent(self, event):
        # 实现右键菜单
        menu = QMenu(self)
        back_action = menu.addAction("后退")
        forward_action = menu.addAction("前进")
        reload_action = menu.addAction("刷新")
        stop_action = menu.addAction("停止")
        copy_action = menu.addAction("复制")
        paste_action = menu.addAction("粘贴")
        cut_action = menu.addAction("剪切")
        select_all_action = menu.addAction("全选")
        open_in_new_tab_action = menu.addAction("在新标签页打开链接")  # 新增动作

        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action == back_action:
            self.back()
        elif action == forward_action:
            self.forward()
        elif action == reload_action:
            self.reload()
        elif action == stop_action:
            self.stop()
        elif action == copy_action:
            self.page().triggerAction(QWebEnginePage.Copy)
        elif action == paste_action:
            self.page().triggerAction(QWebEnginePage.Paste)
        elif action == cut_action:
            self.page().triggerAction(QWebEnginePage.Cut)
        elif action == select_all_action:
            self.page().triggerAction(QWebEnginePage.SelectAll)
        elif action == open_in_new_tab_action:
            self.open_link_in_new_tab()
            
    def copy_selected_text(self):
        selected_text = self.selectedText()
        if selected_text:
            clipboard = QApplication.clipboard()
            clipboard.setText(selected_text)

    def paste_text(self):
        clipboard = QApplication.clipboard()
        text = clipboard.text()
        if text:
            # 使用JavaScript执行粘贴操作
            self.page().runJavaScript(f"document.execCommand('paste');")

    def cut_text(self):
        # 使用JavaScript执行剪切操作
        self.page().runJavaScript("""
            var sel = window.getSelection();
            if (sel.rangeCount && sel.getRangeAt(0).deleteContents()) {
                var clipboardData = event.clipboardData || window.clipboardData;
                clipboardData.setData('Text', sel.toString());
            }
        """)

    def mouseDoubleClickEvent(self, event):
        if event.button() == Qt.LeftButton and self.hoveredLink:
            self.openLinkInNewTabSignal.emit(QUrl(self.hoveredLink))
        else:
            super().mouseDoubleClickEvent(event)

    def linkHovered(self, url):
        self.hoveredLink = url

    def open_link_in_new_tab(self):
        if self.hoveredLink:
            self.openLinkInNewTabSignal.emit(QUrl(self.hoveredLink))

    def loadStarted(self):
        self.pageLoaded.emit(False)

    def loadFinished(self, status):
        if status:
            self.pageLoaded.emit(True)

    def select_all_text(self):
        self.triggerPageAction(QWebEnginePage.SelectAllAction)


    def linkClicked(self, url):
        # 只在网页加载完成后才处理链接点击事件
        if self.page().isLoading():
            QMessageBox.information(self, "提示", "页面正在加载中,请稍候再试。")
            return

        try:
            # 检查URL是否有效
            if not url.isValid():
                raise ValueError("无效的URL")

            # 检查URL是否使用安全协议
            if url.scheme() not in ["http", "https"]:
                raise ValueError("不安全的URL协议")

            # 获取用户设置,决定是否在新标签页中打开链接
            open_in_new_tab = self.shouldOpenInNewTab(url)

            if open_in_new_tab:
                self.openLinkInNewTab(url)
            else:
                self.setUrl(url)  # 在当前标签页打开链接

        except ValueError as ve:
            logging.error(f"ValueError: {ve}")
            QMessageBox.warning(self, "警告", str(ve))

        except TypeError as te:
            logging.error(f"TypeError: {te}")
            QMessageBox.critical(self, "错误", str(te))

        except Exception as e:
            logging.error(f"Exception: {e}")
            error_message = f"{e.__class__.__name__}: {e} - 无法打开链接。"
            QMessageBox.critical(self, "错误", error_message)

        # 记录点击链接的行为
        logging.info(f"点击链接: {url.toString()}")

    def shouldOpenInNewTab(self, url):
        # 获取用户设置的逻辑(这里简单返回True,实际应用中可能从配置文件或用户选项中获取)
        return self.settings.value("openLinksInNewTab", True, type=bool)
        return True

    def openLinkInNewTab(self, url):
        parent_widget = self.window()
        if isinstance(parent_widget, QMainWindow):
            new_tab_title = url.host() or "New Tab"
            try:
                # 假设 parent_widget 有一个 add_new_tab 方法来添加新标签页
                parent_widget.add_new_tab(url.toString(), new_tab_title)
            except AttributeError:
                raise TypeError("Parent widget does not support creating new tabs.")
        else:
            raise TypeError("Parent widget is not a QMainWindow.")

    def loadStarted(self):
        self.pageLoaded.emit(False)  # 网页开始加载时发射信号,标记为未加载完成

    def loadFinished(self, status):
        if status:
            self.pageLoaded.emit(True)  # 网页加载完成时发射信号,标记为已加载完成


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()

        self.tabWidget = QTabWidget()  # 使用QTabWidget来管理多个标签页
        self.tabWidget.setTabBar(QTabBar(self.tabWidget))  # 自定义TabBar以便后续扩展
        self.tabWidget.setTabsClosable(True)  # 允许关闭标签页
        self.tabWidget.tabCloseRequested.connect(self.close_tab)  # 连接关闭标签的信号

        # 创建一个工具栏
        self.toolbar = QToolBar("工具栏")
        self.addToolBar(self.toolbar)

        # 添加现有动作
        self.back_button = QPushButton('后退')
        self.back_button.clicked.connect(self.navigate_back)
        self.toolbar.addWidget(self.back_button)

        self.forward_button = QPushButton('前进')
        self.forward_button.clicked.connect(self.navigate_forward)
        self.toolbar.addWidget(self.forward_button)

        self.reload_button = QPushButton('刷新')
        self.reload_button.clicked.connect(self.navigate_reload)
        self.toolbar.addWidget(self.reload_button)

        # 创建一个水平布局,用于放置网址输入框和显示框
        self.url_layout = QHBoxLayout()


        self.urlbar = QLineEdit()
        self.urlbar.returnPressed.connect(self.navigate_to_url)
        self.toolbar.addWidget(self.urlbar)

        self.url_display = QLabel()
        self.toolbar.addWidget(self.url_display)

        self.search_button = QPushButton('搜索')
        self.search_button.clicked.connect(self.navigate_to_url)
        self.toolbar.addWidget(self.search_button)

        # 创建一个列表控件,用于显示历史记录
        self.history_list = QListWidget()
        self.history_list.setVisible(False)  # 默认隐藏历史记录列表

        # 创建菜单栏和菜单
        self.menu_bar = QMenuBar()
        self.menu = QMenu("菜单")
        self.menu_bar.addMenu(self.menu)
        self.setMenuBar(self.menu_bar)

        # 新增导入HTML文件的动作
        self.import_html_action = QAction('导入HTML文件', self)
        self.import_html_action.triggered.connect(self.import_html_file)
        self.menu.addAction(self.import_html_action)

        # 新增历史记录的动作
        self.history_action = QAction('历史记录', self)
        self.history_action.triggered.connect(self.show_history)
        self.menu.addAction(self.history_action)

        # 创建一个垂直布局,并将工具栏、标签页和历史记录列表放在其中
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.tabWidget)
        self.layout.addWidget(self.history_list)

        self.widget = QWidget()
        self.widget.setLayout(self.layout)

        self.setCentralWidget(self.widget)

        # 初始化时创建一个默认标签页
        self.add_new_tab('https://www.****.com', 'Baidu')

        self.showMaximized()

    def add_new_tab(self, url=None, title='New Tab'):
        """创建一个新的标签页"""
        browser = CustomWebEngineView()
        browser.setParent(self)
        browser.pageLoaded.connect(self.on_page_loaded)
        browser.openLinkInNewTabSignal.connect(self.open_link_in_new_tab)
        if url:
            browser.setUrl(QUrl(url))
        idx = self.tabWidget.addTab(browser, title)
        self.tabWidget.setCurrentIndex(idx)
        return browser

    def on_page_loaded(self, status):
        """
        处理网页加载完成的逻辑
        """
        current_browser = self.tabWidget.currentWidget()
        if current_browser == self.sender() and status:
            self.urlbar.setText(current_browser.url().toString())
            self.tabWidget.setTabText(self.tabWidget.currentIndex(), current_browser.page().title())
            # TODO: 在这里可以处理关于网页加载完成的逻辑,比如启用某些功能。
    def open_link_in_new_tab(self, url):
        self.add_new_tab(url.toString(), "New Tab")

    def handle_link_click(self, url):
        """
        处理网页中的链接点击事件。
        """
        # 在新的标签页中打开链接
        new_tab_title = "New Tab" if not url else url.scheme() or ""
        self.add_new_tab(url=url.toString(), title=new_tab_title)

    def close_tab(self, index):
        """关闭一个标签页"""
        if self.tabWidget.count() > 1:  # 保证至少有一个标签页
            self.tabWidget.removeTab(index)
        else:
            QMessageBox.warning(self, "Warning", "Cannot close the last tab.")

    def navigate_to_url(self):
        url = self.urlbar.text()
        if not url.startswith('http://') and not url.startswith('https://'):
            url = 'http://' + url
        current_browser = self.tabWidget.currentWidget()
        if current_browser:
            current_browser.setUrl(QUrl(url))
            # 记录导航行为
            logging.info(f"导航到: {url}")

    def import_html_file(self):
        """导入HTML文件并在新标签页中打开"""
        file_dialog = QFileDialog()
        file_path, _ = file_dialog.getOpenFileName(self, "选择HTML文件", "", "HTML Files(*.html *.htm)")
        if file_path:
            url = QUrl.fromLocalFile(os.path.abspath(file_path))
            self.add_new_tab(url.toString(), os.path.basename(file_path))  # 在新标签页中打开

    def show_history(self):
        """显示历史记录列表"""
        if self.history_list.isVisible():
            self.history_list.setVisible(False)
        else:
            self.update_history_list()
            self.history_list.setVisible(True)

    def update_history_list(self):
        """更新历史记录列表"""
        current_browser = self.tabWidget.currentWidget()
        if current_browser:
            history = current_browser.history()
            self.history_list.clear()
            for i in range(history.count()):
                entry = history.itemAt(i)
                self.history_list.addItem(entry.title())

    def navigate_back(self):
        current_browser = self.tabWidget.currentWidget()
        if current_browser and current_browser.history().canGoBack():
            current_browser.back()
            self.update_history_list()  # 更新历史记录列表
            # 记录导航行为
            logging.info("导航回退")

    def navigate_forward(self):
        current_browser = self.tabWidget.currentWidget()
        if current_browser and current_browser.history().canGoForward():
            current_browser.forward()
            self.update_history_list()  # 更新历史记录列表
            # 记录导航行为
            logging.info("导航前进")


    def navigate_stop(self):
        current_browser = self.tabWidget.currentWidget()
        if current_browser:
            current_browser.stop()
            # 记录导航行为
            logging.info("导航停止")

    def navigate_reload(self):
        current_browser = self.tabWidget.currentWidget()
        if current_browser:
            current_browser.reload()
            # 记录导航行为
            logging.info("导航刷新")

if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

注意事项:

代码中对于用户设置(如是否在新标签页打开链接)的获取逻辑较为简单,实际应用中可能需要更完善的设置读取和保存机制。
历史记录的显示和管理功能相对简单,可能需要进一步优化,如记录更多信息(访问时间等)、提供删除历史记录的功能。
对于网页加载过程中的异常处理,虽然有记录日志,但可能需要更友好的用户提示和错误恢复机制。
导入 HTML 文件的功能没有对文件的有效性和内容进行更多的检查和处理。
整体界面的美观和交互性可以进一步改进,以提供更好的用户体验。
代码的注释可以进一步丰富,以提高代码的可理解性和可维护性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yehaiwz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值