简体   繁体   English

如何将 Ipyleaflet map 添加到 PyQt5 应用程序?

[英]How to add an Ipyleaflet map to a PyQt5 application?

I've created an app using PyQt5 that (amongst others) displays a map.我使用 PyQt5 创建了一个应用程序,该应用程序(除其他外)显示 map。

For the map widget, I've used 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 instead.对于 map 小部件,我使用了 pyqtlet ,但我开始意识到这个 package 真的很有限(我想显示不同的层,使用可拖动的标记等),所以我想转换到ipyleaflet .

Except that I cannot get my map to show in the app!除了我无法让我的 map 在应用程序中显示!

The original code was like so:原来的代码是这样的:

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_())
    

I'm then trying to use this to change to ipyleaflet:然后我尝试使用它来更改为 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_())

But the addition of the map in the layout doesn't work, I get this error message:但是在布局中添加 map 不起作用,我收到以下错误消息:

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'

Does anybody know I can add the ipyleaflet map to my application?有人知道我可以将 ipyleaflet map 添加到我的应用程序中吗?

The problem is that you're trying to add self.map to the layout but since it isn't derived from a QWidget, it doesn't work and hence the error.问题是您正在尝试将self.map添加到布局中,但由于它不是从 QWidget 派生的,因此它不起作用,因此出现错误。 I can see that you've imported QtWebEngineWidgets but it isn't being used.我可以看到你已经导入QtWebEngineWidgets但它没有被使用。 To embed a Jupyter widget, you could generate HTML that contains the ipyleaflet widget and then add the HTML to QtWebEngineWidgets.QWebEngineView .要嵌入 Jupyter 小部件,您可以生成包含 ipyleaflet 小部件的 HTML,然后将 HTML 添加到QtWebEngineWidgets.QWebEngineView Below is a simple implementation of adding Jupyter widgets to HTML from the documentation :下面是从文档中将 Jupyter 小部件添加到 HTML 的简单实现:

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_())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM