如何将值从 JS 传递回 Python
Posted
技术标签:
【中文标题】如何将值从 JS 传递回 Python【英文标题】:How to pass back value from JS to Python 【发布时间】:2018-04-10 18:01:50 【问题描述】:我想用 Python 显示一个 2D 地图,然后用 Python 代码中的游标坐标做一些事情。但是,我无法获得 Python 部分的坐标。 这是我的代码:
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWidgets import QWidget,QVBoxLayout, QApplication
from PyQt5.QtWebChannel import QWebChannel
import bs4
maphtml = '''
<!DOCTYPE HTML>
<!DOCTYPE HTML>
<html>
<head>
<meta name="robots" content="index, all" />
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<title>WebGL Earth API - Side-by-side - Basic Leaflet compatibility</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script src="http://www.webglearth.com/v2/api.js"></script>
<script>
var backend;
new QWebChannel(qt.webChannelTransport, function (channel)
backend = channel.objects.backend;
);
function init()
var m = ;
start_(L, 'L');
start_(WE, 'WE');
function start_(API, suffix)
var mapDiv = 'map' + suffix;
var map = API.map(mapDiv,
center: [51.505, -0.09],
zoom: 4,
dragging: true,
scrollWheelZoom: true,
proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
);
m[suffix] = map;
//Add baselayer
API.tileLayer('http://s.tile.openstreetmap.org/z/x/y.png',
attribution: '© OpenStreetMap contributors'
).addTo(map);
//Add TileJSON overlay
var json = "profile": "mercator", "name": "Grand Canyon USGS", "format": "png", "bounds": [-112.26379395, 35.98245136, -112.10998535, 36.13343831], "minzoom": 10, "version": "1.0.0", "maxzoom": 16, "center": [-112.18688965, 36.057944835, 13], "type": "overlay", "description": "", "basename": "grandcanyon", "tilejson": "2.0.0", "sheme": "xyz", "tiles": ["http://tileserver.maptiler.com/grandcanyon/z/x/y.png"];
if (API.tileLayerJSON)
var overlay2 = API.tileLayerJSON(json, map);
else
//If not able to display the overlay, at least move to the same location
map.setView([json.center[1], json.center[0]], json.center[2]);
//Add simple marker
var marker = API.marker([json.center[1], json.center[0]]).addTo(map);
marker.bindPopup(suffix, 50);
marker.openPopup();
//Print coordinates of the mouse
map.on('mousemove', function(e)
document.getElementById('coords').innerHTML = e.latlng.lat + ', ' + e.latlng.lng;
backend.print(e.latlng.lat)
);
//Synchronize view
m['L'].on('move', function(e)
var center = m['L'].getCenter();
var zoom = m['L'].getZoom();
m['WE'].setView([center['lat'], center['lng']], zoom);
);
</script>
<style>
html, bodypadding: 0; margin: 0; overflow: hidden;
#mapL, #mapWE position:absolute !important; top: 0; right: 0; bottom: 0; left: 0;
background-color: #fff; position: absolute !important;
#mapL right: 0%;
#mapWE left: 100%;
#coords position: absolute; bottom: 0;
</style>
</head>
<body onload="javascript:init()">
<div id="mapL"></div>
<div id="mapWE"></div>
<div id="coords"></div>
</body>
</html>
'''
class Browser(QApplication):
def __init__(self):
QApplication.__init__(self, [])
self.window = QWidget()
self.window.setWindowTitle("Serial GPS Emulator");
self.web = QWebEngineView(self.window)
self.web.setHtml(maphtml)
self.layout = QVBoxLayout(self.window)
self.layout.addWidget(self.web)
self.window.show()
self.exec()
Browser()
如果代码保留在一个文件中会很好,但如果完全不可能在一次拆分中完成它是可以接受的。 我想解决 mny 问题的第一步是从 HTML/JS 部分调用一个函数,因为 backend.print("test") 也不起作用。 我还注意到 self.exec() 阻止了其余代码,有没有办法在地图运行时执行任何其他代码?谢谢!
【问题讨论】:
【参考方案1】:我没有看到您在代码中的哪个位置通过了后端。
在这种情况下,您必须创建一个可以注入的Backend
类,并且对于要调用的方法,它必须是一个槽,为此您必须使用pyqtSlot()
设置,它接收的参数取决于在e.latlng
的情况下,您发送的是QJsonValue
。在插槽中,您必须分离必要的部分。
import sys
from PyQt5.QtCore import pyqtSlot, QObject, QJsonValue, pyqtSignal, QTimer
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication
maphtml = '''
<!DOCTYPE HTML>
<!DOCTYPE HTML>
<html>
<head>
<meta name="robots" content="index, all" />
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
<title>WebGL Earth API - Side-by-side - Basic Leaflet compatibility</title>
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
<script src="http://www.webglearth.com/v2/api.js"></script>
<script>
var backend;
new QWebChannel(qt.webChannelTransport, function (channel)
backend = channel.objects.backend;
console.log(backend);
);
function init()
var m = ;
start_(L, 'L');
start_(WE, 'WE');
function start_(API, suffix)
var mapDiv = 'map' + suffix;
var map = API.map(mapDiv,
center: [51.505, -0.09],
zoom: 4,
dragging: true,
scrollWheelZoom: true,
proxyHost: 'http://srtm.webglearth.com/cgi-bin/corsproxy.fcgi?url='
);
m[suffix] = map;
//Add baselayer
API.tileLayer('http://s.tile.openstreetmap.org/z/x/y.png',
attribution: '© OpenStreetMap contributors'
).addTo(map);
//Add TileJSON overlay
var json = "profile": "mercator", "name": "Grand Canyon USGS", "format": "png", "bounds": [-112.26379395, 35.98245136, -112.10998535, 36.13343831], "minzoom": 10, "version": "1.0.0", "maxzoom": 16, "center": [-112.18688965, 36.057944835, 13], "type": "overlay", "description": "", "basename": "grandcanyon", "tilejson": "2.0.0", "sheme": "xyz", "tiles": ["http://tileserver.maptiler.com/grandcanyon/z/x/y.png"];
if (API.tileLayerJSON)
var overlay2 = API.tileLayerJSON(json, map);
else
//If not able to display the overlay, at least move to the same location
map.setView([json.center[1], json.center[0]], json.center[2]);
//Add simple marker
var marker = API.marker([json.center[1], json.center[0]]).addTo(map);
marker.bindPopup(suffix, 50);
marker.openPopup();
//Print coordinates of the mouse
map.on('mousemove', function(e)
document.getElementById('coords').innerHTML = e.latlng.lat + ', ' + e.latlng.lng;
backend.print(e.latlng)
);
//Synchronize view
m['L'].on('move', function(e)
var center = m['L'].getCenter();
var zoom = m['L'].getZoom();
m['WE'].setView([center['lat'], center['lng']], zoom);
);
</script>
<style>
html, bodypadding: 0; margin: 0; overflow: hidden;
#mapL, #mapWE position:absolute !important; top: 0; right: 0; bottom: 0; left: 0;
background-color: #fff; position: absolute !important;
#mapL right: 0%;
#mapWE left: 100%;
#coords position: absolute; bottom: 0;
</style>
</head>
<body onload="javascript:init()">
<div id="mapL"></div>
<div id="mapWE"></div>
<div id="coords"></div>
</body>
</html>
'''
class Backend(QObject):
positionChanged = pyqtSignal(float, float)
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.position = None, None
self.timer = QTimer(self)
self.timer.setInterval(1000)
self.timer.timeout.connect(self.on_timeout)
@pyqtSlot(QJsonValue)
def print(self, val):
coords = val.toObject()
lat, lng = (coords[key].toDouble() for key in ("lat", "lng"))
self.position = lat, lng
if not self.timer.isActive():
self.timer.start()
def on_timeout(self):
self.positionChanged.emit(*self.position)
def foo(lat, lng):
# this function will be called every second.
print(lat, lng)
if __name__ == '__main__':
app = QApplication(sys.argv)
view = QWebEngineView()
view.setWindowTitle("Serial GPS Emulator");
backend = Backend(view)
backend.positionChanged.connect(foo)
channel = QWebChannel()
channel.registerObject('backend', backend)
view.page().setWebChannel(channel)
view.setHtml(maphtml)
view.show()
sys.exit(app.exec_())
【讨论】:
非常感谢!我需要一些时间来理解您的解决方案,但看到它的工作原理真是太棒了! 你能帮我实现一个无限循环,这样我就可以每秒处理坐标吗?我的目标是构建一个发送光标坐标的 GPS NMEA 模拟器。这意味着它应该每秒发送一个带有最新坐标的 NMEA 语句。我已经想出了如何使用 pyserial 发送串行数据,所以我只需要每秒调用一个函数 sendNMEAmsg(lat,lng)。目前我只能在 mosemove 上执行一个功能。你怎么能改变它? @KASA 你要什么坐标?如果是鼠标,在鼠标离开屏幕的情况下,它会发送什么坐标? @KASA 另外,每次必要时发送信息也不是更好,在这种情况下,每次坐标变化时,而不是每秒。 串行消息的接收者要求每秒更新一次,即使坐标没有改变。我指的是地图上的坐标。以上是关于如何将值从 JS 传递回 Python的主要内容,如果未能解决你的问题,请参考以下文章
如何将值从视图传递到不可编辑的字段django python