Display Google Maps with wx.html2.WebView

Hello! I used wxWidgets back when it was still called wxWindows. I am starting a new project after many years and am excited about the possibilities with wxPython. Unfortunately I have almost immediately run into a problem.

As the subject says, I am trying to use the Google Maps API through a wx.html2.WebView widget. I am currently working in Debian with gtk3. I built wxWidgets and wxPython myself. My simple app is below. Notice that I am writing out the page’s HTML to the file web.html.

The app runs, but instead of a map, I get an “Oops! Something went wrong. This app didn’t load Google Maps correctly.” If I load web.html in Firefox or Chrome, which contains the same HTML that I gave to wx.html2.WebView, I get the map that I expect.

The HTML, CSS, JavaScript, and app key are all from two Google examples. The only thing that I changed was to embed the CSS and JS in the HTML instead of using separate files.

I would be very grateful to anyone that can point out to me what I’m doing wrong.

Thanks!
John

#!/usr/bin/env python3

import wx
import wx.html2

HTML_STRING = """<html>
  <head>
    <title>Simple Markers</title>
    <style>
      /* 
       * Always set the map height explicitly to define the size of the div element
       * that contains the map. 
       */
      #map {
        height: 100%;
      }

      /* 
       * Optional: Makes the sample page fill the window. 
       */
      html,
      body {
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
    <script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
    <!-- playground-hide -->
    <script>
      const process = { env: {} };
      process.env.GOOGLE_MAPS_API_KEY =
        "AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg";
    </script>
    <!-- playground-hide-end -->
    <script>
      function initMap() {
        const myLatLng = { lat: -25.363, lng: 131.044 };
        const map = new google.maps.Map(document.getElementById("map"), {
          zoom: 4,
          center: myLatLng,
        });

        new google.maps.Marker({
          position: myLatLng,
          map,
          title: "Hello World!",
        });
      }

      window.initMap = initMap;
    </script>
  </head>
  <body>
    <h3>Put a pin in it</h3>
    <hr/>
    <div id="map"></div>
    <hr/>

    <!-- 
     The `defer` attribute causes the callback to execute after the full HTML
     document has been parsed. For non-blocking uses, avoiding race conditions,
     and consistent behavior across browsers, consider loading using Promises
     with https://www.npmjs.com/package/@googlemaps/js-api-loader.
    -->
    <script
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB41DRUbKWJHPxaFjMAwdrzWzbVKartNGg&callback=initMap&v=weekly"
      defer
    ></script>
  </body>
</html>
"""


class MyBrowser(wx.Frame):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.browser = wx.html2.WebView.New(self)
        sizer.Add(self.browser, 1, wx.EXPAND, 10)
        self.SetSizer(sizer)
        self.SetSize((700, 700))


def main():
    with open('map.html', 'w') as out:
        print(HTML_STRING, file=out)

    app = wx.App()
    main_window = MyBrowser(None, -1)
    main_window.browser.SetPage(HTML_STRING, "")
    main_window.Show()
    app.MainLoop()


if __name__ == '__main__':
    main()

I’ve found that anything more complex than simple html usually needs to know the context that the page was loaded from. When using SetPage there is no context at all, but if you give it one like the following, then your example works for me:

    base = os.getcwd()
    main_window.browser.SetPage(HTML_STRING, f"file://{base}")

If you had loaded from the file instead using LoadURL, then the context is implicit so this works too:

    mapPage = os.path.abspath("map.html")
    main_window.browser.LoadURL(f"file://{mapPage}")

Thank you! Now all I have to do is get this to work under Windows and I’ll be home free! :sweat_smile: