PyQt6 与 Pyecharts 交互:将鼠标点击 html 页面某点时的提示框信息在 PyQt 界面显示

版本信息

PyQt6:6.6.1
pyqt6-tools:6.4.2.3.3
PyQt6-WebEngine:6.6.0

pyqt6 官方文档:Reference Guide — PyQt Documentation v6.6.0

1. 知识点:信号与槽函数

在 PyQt 中,信号signal和槽slot是用来处理事件的机制。

信号是一个对象,当它被发射时,会通知所有连接的槽。槽是一个函数,当信号被发射时,它会被调用。

PyQt中常见控件及其对应的信号举例:

QPushButton:clicked()

QCheckBox:stateChanged() 

QLineEdit:textChanged() 

QSlider:valueChanged() 

QTableView:doubleClicked()

(1) 信号只能在QObject的子类中定义使用pyqtSignal函数可以创建未绑定的信号作为类的属性,信号可以传递多个参数,且可以指定参数类型(标准的python数据类型)。

(2) 与信号关联的槽函数可以实现当信号发射时,做什么事情。信号与槽函数通过connet连接;

(3) 使用emit可以发射信号,触发与信号关联的槽函数。

(4) 定义信号时要注意代码位置,不能在初始化中定义信号,否则会遇到报错PyQt5.QtCore.pyqtSignal‘ object has no attribute ‘connect‘,信号应作为类属性而不是实例属性

错误示范

class MyObject(QObject):
   
    def __init__(self):
        super().__init__()
        # 定义信号位置错误!!!
        self.show_infoes_signal = pyqtSignal(str)

正确示范

class MyObject(QObject):
    show_infoes_signal = pyqtSignal(str)
   
    def __init__(self):
        super().__init__()

更多信息参考下方链接

自定义信号signal和槽slot时报错‘PyQt5.QtCore.pyqtSignal‘ object has no attribute ‘connect‘解决方案_pyqt5.qtcore.pyqtsignal' object has no attribute '-CSDN博客

2. 将鼠标点击图上某点时所显示的提示框信息输出到界面PlainTextEdit

1 获取鼠标点击网页元素时,提示框的信息

首先看一下Pyecharts生成的网页内容(右键检查->元素):

由上述信息可知,第一个div的内容即为提示框信息对应的文本内容,可以在HTML文件中增加以下脚本进行确认,结果符合预期。


    <script>
        document.addEventListener("mousemove", function() {
            var web_info = document.getElementsByTagName("div")[0].innerText;
            console.log(web_info)
    })
    </script>

控制台打印结果: 

2 插曲(赶时间可跳过这段):mousePressEvent不能用于QWebEngineView对象

PyQt中,QWebEngineView对象可通过“对象名.page()”函数获得QWebEnginePage对象,QWebEnginePage类有一个runJavaScript方法,这个方法可以通过执行JS语句与页面交互,并可以通过回调函数接收执行结果,其语句如下:

QWebEnginePage.runJavaScript('JS语句', 回调函数)

因而,最初的想法是,通过mousePressEvent监控鼠标行为,当发生鼠标点击行为时,通过runJavaScript直接获取网页上所需变量的值,从而获得我想要的信息。然而尝试后发现,始终没有任何输出。

猜想mousePressEvent这部分有问题,通过如下代码验证:首先创建一个QMainWindow,其中有一个QWebEngineView控件,用于显示网页;然后重写mousePressEvent,如果识别到鼠标点击操作,就在控制台打印一个"yes",通过这种方式,看一下鼠标点击在界面不同位置的效果。运行代码后发现,鼠标点击在QWebEngineView控件内部时,控制台始终没有任何打印,因而上述思路不可行。

# 如下代码属于尝试阶段,可运行体验
from PyQt6.QtWidgets import QApplication, QMainWindow
import sys
import cdsn_show
from PyQt6.QtCore import QUrl, QFileInfo


class Ui_CDSN(cdsn_show.Ui_MainWindow, QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        url = QUrl("file:" + QFileInfo("cdsn_figure.html").absoluteFilePath())
        self.widget_figure.load(url)
        self.show()

    def mousePressEvent(self, event):
        print("yes")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    my_ui = Ui_CDSN()
    sys.exit(app.exec())

3 基于QWebChannel实现PyQt与HTML文件的交互

原理简述:QWebChannel是一种在Web页面和Python代码之间进行通信的机制。它的原理是通过JavaScript和Python之间的桥接,使得Web页面中的JavaScript代码可以调用Python中的代码,并且Python中的代码也可以调用Web页面中的JavaScript代码。

步骤一:下载qwebchannel.js文件

网址:https://github.com/qt/qtwebchannel/tree/dev/examples/webchannel/shared

步骤二:Python部分

(1) Ui界面文件<csdn_show.py>

界面主要包含一个qwebengineview控件widget_figure用于显示网页,一个plaintextedit控件用于显示信息。

from PyQt6 import QtCore, QtGui, QtWidgets
from PyQt6 import QtWebEngineWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(994, 722)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.widget = QtWidgets.QWidget(parent=self.centralwidget)
        self.widget.setGeometry(QtCore.QRect(40, 30, 921, 631))
        self.widget.setObjectName("widget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.widget)
        self.verticalLayout_2.setContentsMargins(0, 0, 0, 0)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.widget_figure = QtWebEngineWidgets.QWebEngineView(parent=self.widget)
        self.widget_figure.setObjectName("widget_figure")
        self.verticalLayout_2.addWidget(self.widget_figure)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(parent=self.widget)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        spacerItem = QtWidgets.QSpacerItem(20, 78, QtWidgets.QSizePolicy.Policy.Minimum,
                                           QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.plainTextEdit = QtWidgets.QPlainTextEdit(parent=self.widget)
        self.plainTextEdit.setObjectName("plainTextEdit")
        self.horizontalLayout.addWidget(self.plainTextEdit)
        self.verticalLayout_2.addLayout(self.horizontalLayout)
        self.verticalLayout_2.setStretch(0, 8)
        self.verticalLayout_2.setStretch(1, 3)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 994, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.label.setText(_translate("MainWindow", "提示信息"))

(2) Pycharts生成的HTML图

不做展开,请自行准备,参考Pyqt6与Pyecharts交互 -- 持续更新_pyside6 pyqt6 pyecharts-CSDN博客

(3) 程序运行文件<csdn_main.py>

说明:InteractObject部分

1. 创建一个QObject子类,后续会将其注册到QWebChannel对象中(必须是QObject子类)。

2. 定义了一个名为receive_message的槽函数,该槽函数将打印并返回传递给它的数据。

说明:Ui_CSDN部分

1. 创建了一个QWebChannel对象,并将其绑定到QWebEngineView对象上,以便在HTML页面和Python代码之间传递数据。

2. 创建了一个InteractObject对象self.obj,并将这个对象注册到WebChannel中,注册名为interact_object,HTML页面可以通过WebChannel对象调用InteractObject对象的方法和属性,借此传递页面消息给PyQt或获取来自PyQt的信息。

3. 将自定义信号self.obj.mySignal与自定义槽函数updata_plain_text_edit连接起来,以便将接收到的信息显示在PyQt界面上。

import sys
import csdn_show
from PyQt6.QtWebChannel import QWebChannel
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtCore import QUrl, QFileInfo, pyqtSlot, QObject, pyqtSignal


class InteractObject(QObject):
    mySignal = pyqtSignal(str)

    def __init__(self):
        super().__init__()

    @pyqtSlot(str, result=str)
    def receive_message(self, message):
        if isinstance(message, str):
            print(f"Received message from html:\n{message}")
            self.mySignal.emit(message)
            return message
        else:
            print("no message")
            return "no message"


class Ui_CSDN(cdsn_show.Ui_MainWindow, QMainWindow):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.channel = QWebChannel()
        self.widget_figure.page().setWebChannel(self.channel)
        self.obj = InteractObject()
        self.channel.registerObject("interact_object", self.obj)

        url = QUrl("file:" + QFileInfo("cdsn_figure.html").absoluteFilePath())
        self.widget_figure.load(url)
        self.show()
        self.obj.mySignal.connect(self.updata_plain_text_edit)

    @pyqtSlot(str)
    def updata_plain_text_edit(self, message):
        try:
            self.plainTextEdit.setPlainText(message)
        except Exception as e:
            self.plainTextEdit.setPlainText('No Message')


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = Ui_CSDN()
    app.exec()

步骤三:Javascript部分

1. 引入qwebchannel.js文件,文件路径请根据实际情况调整。

2. 实例化QWebChannel(QWebChannel在qwebchannel.js中定义,因而需要先引入.js文件),并在回调函数中,通过channel.objects.bridge获取来自PyQt的共享对象interact_object,代码中的interact_object即为第二步注册时使用的名字,务必与前面注册时一致。

3. 增加鼠标点击事件监听,当发生鼠标点击事件时,将页面值通过interact_object返回给PyQt。

    <script type="text/javascript" src="D:/0_study_code/Sites3DFigDisplay/pyecharts-assets-master/assets/qwebchannel.js"></script>
    <script>
        document.addEventListener("DOMContentLoaded", function() {
	        //把对象赋值到JS中
	        var channel = new QWebChannel(window.qt.webChannelTransport, function(channel) {
		    // Get interact_object
		    window.interact_object = channel.objects.interact_object;
	    });
    });
        document.addEventListener("click", function() {
            var web_info = document.getElementsByTagName("div")[0].innerText;
            window.interact_object.receive_message(web_info);
    })
        
    </script>

pyecharts-html文件最终内容一览

QWebchannel部分研究了好久,感谢博主【天马小Q】的博文,让我豁然开朗,放在下面给大家参考。

参考链接:PyQt5 QWebEngineView中实现Python和JavaScript交互数据传递demo_pyqt5 qwebengineview js-CSDN博客

步骤四:编写函数,每次Pyecharts生成图像后,自动添加上述JS脚本

def add_content(filename):
    js_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "pyecharts-assets-master",
                                               "assets", "qwebchannel.js"))
    html_js_path = str(js_path).replace("\\", r'/')
    add_content = [
        '\t\t<script type="text/javascript" ' + f'src="{html_js_path}"></script>\n',
        '\t<script>\n',
        '\t\tdocument.addEventListener("DOMContentLoaded", function() {\n',
        '\t\t\tvar channel = new QWebChannel(window.qt.webChannelTransport, function(channel) {\n',
        '\t\t\twindow.interact_object = channel.objects.interact_object;\n',
        '\t\t\t});\n',
        '\t\t});\n',
        '\t\tdocument.addEventListener("click", function() {\n',
        '\t\t\tvar web_info = document.getElementsByTagName("div")[0].innerText;\n',
        '\t\t\twindow.interact_object.receive_message(web_info);\n',
        '\t\t})\n',
        '\t</script>\n']
    with open(filename, 'r') as file:
        html_content = file.readlines()

    insert_index = 7
    target_content = 'pyecharts-assets-master'
    if target_content not in html_content[5]:
        insert_index = -1
        for i in range(len(html_content)):
            if target_content in html_content[i]:
                insert_index = i + 1
                break
    if insert_index == '-1':
        print('无法确定内容插入位置')
        return False
    else:
        print('可进行内容插入')

    new_html_content = html_content[:insert_index] + add_content + html_content[insert_index:]
    with open(filename, 'w') as file:
        # 将修改后的HTML内容写回文件
        file.writelines(new_html_content)

    return True

3. 效果展示

当鼠标点击图上点时,提示框中的信息会同步显示在下方PlainTextEdit框中。

如果出现类似下图的报错,大概率是标黄的路径写的有问题,核对一下。

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PyQt6是一个用于Python编程语言的GUI工具包,它允许开发人员在Windows、Mac和Linux等多个平台上创建功能强大的图形用户界面(GUI)应用程序。对于那些希望快速开发和实战的开发人员来说,PyQt6是一个非常有用的工具。 首先,你可以从官方网站上下载PyQt6的源码和文档。官方网站提供了最新的稳定版本和先前版本的下载选项。你可以根据自己的操作系统选择适当的版本进行下载。 在下载完成后,你可以按照文档中的说明进行安装。通常情况下,你只需要运行安装程序并按照步骤完成安装过程。一旦安装完成,你就可以开始在PyQt6中进行快速开发和实战。 PyQt6提供了丰富的GUI组件和工具,使得开发过程更加简单和高效。它包括了各种各样的控件,如按钮、文本框、标签、列表框等等,你可以通过简单的代码来创建和布局这些控件。此外,PyQt6还提供了各种丰富的功能,如绘图、动画、数据存储等,使得你可以轻松地开发出具有复杂功能和交互性的应用程序。 另外,PyQt6还提供了丰富的文档和示例代码。这些文档和示例代码将指导你如何正确地使用PyQt6的各种功能和组件。你可以通过阅读文档和运行示例代码来学习和掌握PyQt6的开发技巧和最佳实践。 总之,PyQt6是一个功能强大的GUI工具包,可以帮助你快速开发和实战。你可以从官方网站下载并安装PyQt6,并通过阅读文档和运行示例代码来学习和掌握它的使用方法。无论是初学者还是有经验的开发人员,PyQt6都是一个优秀的选择。 ### 回答2: 在进行PyQt6快速开发与实战之前,首先需要下载并安装PyQt6库。PyQt6是一个功能强大的Python框架,它提供了丰富的GUI编程工具和功能,可以帮助开发者快速构建界面丰富、交互性强的应用程序。 要下载PyQt6库,可以通过以下步骤进行: 1. 打开Python的官方网站(https://www.python.org/),并下载和安装Python解释器。请确保安装的是Python 3.x版本,因为PyQt6只支持Python 3。 2. 打开命令行终端(Windows可以使用Cmd、PowerShell等;Mac和Linux可以使用终端),输入以下命令安装PyQt6: ``` pip install PyQt6 ``` 这会自动从Python包索引下载并安装PyQt6库。如果你使用的是conda环境,也可以使用类似的命令进行安装。 3. 安装完成后,你就可以在Python脚本中导入并使用PyQt6库了。只需在脚本中加入以下语句即可: ```python from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel # 在这里写下你的代码 ``` 这样,你就可以根据自己的需要使用PyQt6工具和功能进行开发了。 需要注意的是,PyQt6的下载速度可能会受到网络环境的影响。如果下载速度较慢,可以尝试更换源镜像,或者使用代理服务器来加快下载速度。 总之,通过以上步骤,你可以快速下载并安装PyQt6库,为实战开发提供强大的GUI编程工具和功能。希望这个回答对你有所帮助! ### 回答3: PYQT6是一种用于创建图形用户界面(GUI)应用程序的Python库。它基于了Qt框架,并提供了丰富的功能和工具,可以快速开发和部署可视化的应用程序。 要下载PYQT6快速开发与实战的内容,可以采取以下几个步骤: 1. 在互联网浏览器中搜索"PYQT6快速开发与实战",可以找到相关的网站或在线资源。 2. 访问可信赖的网站,如官方文档、编程论坛或云存储平台。 3. 在网站中搜索PYQT6快速开发与实战的下载链接或资源。 4. 点下载链接,根据提示选择合适的版本和操作系统。 5. 下载完成后,可以解压缩文件并阅读其中的文档。通常会包含示例代码、教程和其他辅助材料。 6. 根据实际需要,可以在自己的开发环境中调用PYQT6库并开始实际的应用程序开发。 请注意,在下载PYQT6快速开发与实战的过程中要保持警惕,避免从不可靠的来源下载,以防止安全风险和恶意软件的感染。 最后,推荐在学习和使用PYQT6过程中,多查阅相关的文档和教程,充分利用PYQT6提供的功能和工具,以便更好地进行快速开发和实战。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值