我目前正在寻找一种使用Python和HTML/CSS/JS创建GUI桌面应用程序的方法,其中使用了PyQt5的QWebEngineView。
在我的小演示应用程序中,我使用QWebChannel将Python QObject发布到JavaScript端,以便数据可以共享并来回传递。到目前为止,共享和连接插槽和信号都能正常工作。
然而,我遇到了简单(属性)值同步的困难。从我所读的内容来看,解决这个问题的方法是通过装饰的getter和setter函数在共享的QObject中实现pyqtProperty,同时在setter中发出一个额外的信号,用于在值更改时通知JavaScript。以下代码展示了这一点,到目前为止它也能正常工作:
import sys
from PyQt5.QtCore import QObject, pyqtSlot, pyqtProperty, pyqtSignal
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEnginePage
class HelloWorldHtmlApp(QWebEngineView):
html = '''
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script>
var backend;
new QWebChannel(qt.webChannelTransport, function (channel) {
backend = channel.objects.backend;
});
</script>
</head>
<body> <h2>HTML loaded.</h2> </body>
</html>
'''
def __init__(self):
super().__init__()
# setup a page with my html
my_page = QWebEnginePage(self)
my_page.setHtml(self.html)
self.setPage(my_page)
# setup channel
self.channel = QWebChannel()
self.backend = self.Backend(self)
self.channel.registerObject('backend', self.backend)
self.page().setWebChannel(self.channel)
class Backend(QObject):
""" Container for stuff visible to the JavaScript side. """
foo_changed = pyqtSignal(str)
def __init__(self, htmlapp):
super().__init__()
self.htmlapp = htmlapp
self._foo = "Hello World"
@pyqtSlot()
def debug(self):
self.foo = "I modified foo!"
@pyqtProperty(str, notify=foo_changed)
def foo(self):
return self._foo
@foo.setter
def foo(self, new_foo):
self._foo = new_foo
self.foo_changed.emit(new_foo)
if __name__ == "__main__":
app = QApplication.instance() or QApplication(sys.argv)
view = HelloWorldHtmlApp()
view.show()
app.exec_()
在调试器连接的情况下,我可以在JavaScript控制台中调用backend.debug()
槽,从而导致backend.foo
的值在之后为"I modified foo!",这意味着Python代码成功地更改了JavaScript变量。
但是这有点繁琐。对于我想要共享的每个值,我都需要:
- 创建一个内部变量(这里是
self._foo
) - 创建一个getter函数
- 创建一个setter函数
- 在
QObject
的主体中创建一个信号 - 在setter函数中显式地发射此信号
有没有更简单的方法来实现这一点?理想情况下,是否有某种一行声明的方式?也许使用类或函数将其打包起来?我该如何在稍后将其绑定到QObject
上?我在考虑类似以下的东西:
# in __init__
self.foo = SyncedProperty(str)
这是否可能?感谢您的想法!
type(QObject)
用作元类的基础,或者在存在通知信号之前延迟pyqtProperty
的__init__
,或者在getter和setter中访问inst
(“第二个self”)以获取绑定信号的控制权。但你做到了这一切,而且它像魔法一样运行。非常感谢@ekhumoro! :) - Jeronimo