如何将 Ipyleaflet 映射添加到 PyQt5 应用程序?
How to add an Ipyleaflet map to a PyQt5 application?
我使用 PyQt5 创建了一个应用程序,它(除其他外)显示地图。
对于地图小部件,我使用了 pyqtlet, but I'm starting to realize that this package is really limited (I want to show different layers, to use a draggable marker, etc.), so I want to transition to ipyleaflet。
除了我无法在应用程序中显示我的地图!
原代码是这样的:
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from pyqtlet import L, MapWidget
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
self.mapWidget = MapWidget()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView(self.base_coords, zoom=10)
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}'
).addTo(self.map) # ArcGIS_topo layer
self.marker = L.marker(self.base_coords)
self.marker.bindPopup('This is my marker')
self.map.addLayer(self.marker)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
sys.exit(app.exec_())
然后我尝试使用它来更改为 ipyleaflet:
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from PyQt5 import QtWebEngineWidgets
from ipyleaflet import Map, Marker, LayersControl, basemaps
from ipywidgets import HTML
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
# Working with the maps with ipyleaflet
self.map = Map(center=self.base_coords, basemaps=basemaps.Esri.WorldTopoMap, zoom=10)
self.layout.addWidget(self.map)
self.marker = Marker(location=self.base_coords)
self.marker.popup = HTML(value='This is my marker')
self.map.add_layer(self.marker)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
sys.exit(app.exec_())
但是在布局中添加地图不起作用,我收到此错误消息:
Traceback (most recent call last):
File "G:\venv\lib\site-packages\IPython\core\interactiveshell.py", line 3418, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-bd466e04ab02>", line 1, in <module>
runfile('G:/Application/short_app - Copie.py', wdir='G:/Application')
File "C:\Program Files\JetBrains\PyCharm 2020.2.3\plugins\python\helpers\pydev\_pydev_bundle\pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "C:\Program Files\JetBrains\PyCharm 2020.2.3\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "G:/Application/short_app - Copie.py", line 29, in <module>
widget = MapWindow(base_coords)
File "G:/Application/short_app - Copie.py", line 19, in __init__
self.layout.addWidget(self.map)
TypeError: addWidget(self, QWidget, stretch: int = 0, alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 'Map'
有人知道我可以将 ipyleaflet 地图添加到我的应用程序吗?
问题是您试图将 self.map
添加到布局中,但由于它不是从 QWidget 派生的,因此它不起作用,因此出现错误。我可以看到您已经导入了 QtWebEngineWidgets
但它没有被使用。要嵌入 Jupyter 小部件,您可以生成包含 ipyleaflet 小部件的 HTML,然后将 HTML 添加到 QtWebEngineWidgets.QWebEngineView
。下面是将 Jupyter 小部件添加到 HTML from the documentation:
的简单实现
import sys
import json
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from PyQt5 import QtWebEngineWidgets
from ipyleaflet import Map, Marker, LayersControl, basemaps
from ipywidgets import HTML, IntSlider
from ipywidgets.embed import embed_data
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
# Create QtWebEngineView widget
self.web = QtWebEngineWidgets.QWebEngineView(self)
# Sliders
s1 = IntSlider(max=200, value=100)
s2 = IntSlider(value=40)
# Working with the maps with ipyleaflet
self.map = Map(center=self.base_coords, basemaps=basemaps.Esri.WorldTopoMap, zoom=10)
self.marker = Marker(location=self.base_coords)
self.marker.popup = HTML(value='This is my marker')
self.map.add_layer(self.marker)
data = embed_data(views=[s1, s2, self.map])
html_template = """
<html>
<head>
<title>Widget export</title>
<!-- Load RequireJS, used by the IPywidgets for dependency management -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"
integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA="
crossorigin="anonymous">
</script>
<!-- Load IPywidgets bundle for embedding. -->
<script
data-jupyter-widgets-cdn="https://cdn.jsdelivr.net/npm/"
src="https://unpkg.com/@jupyter-widgets/html-manager@*/dist/embed-amd.js"
crossorigin="anonymous">
</script>
<!-- The state of all the widget models on the page -->
<script type="application/vnd.jupyter.widget-state+json">
{manager_state}
</script>
</head>
<body>
<h1>Widget export</h1>
<div id="first-slider-widget">
<!-- This script tag will be replaced by the view's DOM tree -->
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[0]}
</script>
</div>
<hrule />
<div id="second-slider-widget">
<!-- This script tag will be replaced by the view's DOM tree -->
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[1]}
</script>
</div>
<!-- The ipyleaflet map -->
<div id="ipyleaflet-map">
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[2]}
</script>
</div>
</body>
</html>
"""
manager_state = json.dumps(data['manager_state'])
widget_views = [json.dumps(view) for view in data['view_specs']]
rendered_template = html_template.format(manager_state=manager_state, widget_views=widget_views)
# Set HTML
self.web.setHtml(rendered_template)
# Add webengine to layout and add layout to widget
self.layout.addWidget(self.web)
self.setLayout(self.layout)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
widget.resize(900, 800)
sys.exit(app.exec_())
我使用 PyQt5 创建了一个应用程序,它(除其他外)显示地图。
对于地图小部件,我使用了 pyqtlet, but I'm starting to realize that this package is really limited (I want to show different layers, to use a draggable marker, etc.), so I want to transition to ipyleaflet。
除了我无法在应用程序中显示我的地图!
原代码是这样的:
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from pyqtlet import L, MapWidget
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
self.mapWidget = MapWidget()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView(self.base_coords, zoom=10)
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}'
).addTo(self.map) # ArcGIS_topo layer
self.marker = L.marker(self.base_coords)
self.marker.bindPopup('This is my marker')
self.map.addLayer(self.marker)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
sys.exit(app.exec_())
然后我尝试使用它来更改为 ipyleaflet:
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from PyQt5 import QtWebEngineWidgets
from ipyleaflet import Map, Marker, LayersControl, basemaps
from ipywidgets import HTML
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
# Working with the maps with ipyleaflet
self.map = Map(center=self.base_coords, basemaps=basemaps.Esri.WorldTopoMap, zoom=10)
self.layout.addWidget(self.map)
self.marker = Marker(location=self.base_coords)
self.marker.popup = HTML(value='This is my marker')
self.map.add_layer(self.marker)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
sys.exit(app.exec_())
但是在布局中添加地图不起作用,我收到此错误消息:
Traceback (most recent call last):
File "G:\venv\lib\site-packages\IPython\core\interactiveshell.py", line 3418, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-2-bd466e04ab02>", line 1, in <module>
runfile('G:/Application/short_app - Copie.py', wdir='G:/Application')
File "C:\Program Files\JetBrains\PyCharm 2020.2.3\plugins\python\helpers\pydev\_pydev_bundle\pydev_umd.py", line 197, in runfile
pydev_imports.execfile(filename, global_vars, local_vars) # execute the script
File "C:\Program Files\JetBrains\PyCharm 2020.2.3\plugins\python\helpers\pydev\_pydev_imps\_pydev_execfile.py", line 18, in execfile
exec(compile(contents+"\n", file, 'exec'), glob, loc)
File "G:/Application/short_app - Copie.py", line 29, in <module>
widget = MapWindow(base_coords)
File "G:/Application/short_app - Copie.py", line 19, in __init__
self.layout.addWidget(self.map)
TypeError: addWidget(self, QWidget, stretch: int = 0, alignment: Union[Qt.Alignment, Qt.AlignmentFlag] = Qt.Alignment()): argument 1 has unexpected type 'Map'
有人知道我可以将 ipyleaflet 地图添加到我的应用程序吗?
问题是您试图将 self.map
添加到布局中,但由于它不是从 QWidget 派生的,因此它不起作用,因此出现错误。我可以看到您已经导入了 QtWebEngineWidgets
但它没有被使用。要嵌入 Jupyter 小部件,您可以生成包含 ipyleaflet 小部件的 HTML,然后将 HTML 添加到 QtWebEngineWidgets.QWebEngineView
。下面是将 Jupyter 小部件添加到 HTML from the documentation:
import sys
import json
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QLabel
from PyQt5 import QtWebEngineWidgets
from ipyleaflet import Map, Marker, LayersControl, basemaps
from ipywidgets import HTML, IntSlider
from ipywidgets.embed import embed_data
class MapWindow(QWidget):
def __init__(self, base_coords):
self.base_coords = base_coords
# Setting up the widgets and layout
super().__init__()
self.layout = QVBoxLayout()
self.title = QLabel("<b>This is my title</b>")
self.layout.addWidget(self.title)
# Create QtWebEngineView widget
self.web = QtWebEngineWidgets.QWebEngineView(self)
# Sliders
s1 = IntSlider(max=200, value=100)
s2 = IntSlider(value=40)
# Working with the maps with ipyleaflet
self.map = Map(center=self.base_coords, basemaps=basemaps.Esri.WorldTopoMap, zoom=10)
self.marker = Marker(location=self.base_coords)
self.marker.popup = HTML(value='This is my marker')
self.map.add_layer(self.marker)
data = embed_data(views=[s1, s2, self.map])
html_template = """
<html>
<head>
<title>Widget export</title>
<!-- Load RequireJS, used by the IPywidgets for dependency management -->
<script
src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js"
integrity="sha256-Ae2Vz/4ePdIu6ZyI/5ZGsYnb+m0JlOmKPjt6XZ9JJkA="
crossorigin="anonymous">
</script>
<!-- Load IPywidgets bundle for embedding. -->
<script
data-jupyter-widgets-cdn="https://cdn.jsdelivr.net/npm/"
src="https://unpkg.com/@jupyter-widgets/html-manager@*/dist/embed-amd.js"
crossorigin="anonymous">
</script>
<!-- The state of all the widget models on the page -->
<script type="application/vnd.jupyter.widget-state+json">
{manager_state}
</script>
</head>
<body>
<h1>Widget export</h1>
<div id="first-slider-widget">
<!-- This script tag will be replaced by the view's DOM tree -->
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[0]}
</script>
</div>
<hrule />
<div id="second-slider-widget">
<!-- This script tag will be replaced by the view's DOM tree -->
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[1]}
</script>
</div>
<!-- The ipyleaflet map -->
<div id="ipyleaflet-map">
<script type="application/vnd.jupyter.widget-view+json">
{widget_views[2]}
</script>
</div>
</body>
</html>
"""
manager_state = json.dumps(data['manager_state'])
widget_views = [json.dumps(view) for view in data['view_specs']]
rendered_template = html_template.format(manager_state=manager_state, widget_views=widget_views)
# Set HTML
self.web.setHtml(rendered_template)
# Add webengine to layout and add layout to widget
self.layout.addWidget(self.web)
self.setLayout(self.layout)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
base_coords = [45.783119, 3.123364]
widget = MapWindow(base_coords)
widget.resize(900, 800)
sys.exit(app.exec_())