jaweilbaum / pyqtlet2 Goto Github PK
View Code? Open in Web Editor NEWpyqtlet2 extends pyqtlet which initially brought Leaflet maps to PyQt5 and PySide6.
License: Other
pyqtlet2 extends pyqtlet which initially brought Leaflet maps to PyQt5 and PySide6.
License: Other
Rework the GitHub workflow to enable tests for the project. This requires to workaround the fact that there is no screen connected while the tests run.
Originally posted by k4dmandu March 16, 2022
Is it possible to display bing maps as a layer? There is a plugin for leaflet due to the use of {q} instead of {xyz} in the service url, but how could it be integrated?
Designated plugin for this might be https://github.com/digidem/leaflet-bing-layer
Can be implemented similar in the way the rotated marker was implemented.
I want to switching my application to pyside because of licensing issues.
there exists qtpy, which is a wrapper for pyqt5 and pyside2.
in my fork of pyqtlet2 i added qtpy, so that i can use either pyqt5 or pyside2.
The newest qtpy repository has no QWebEngineScript so i forked this project too (qtpy) and extended it, to get pyqtlet2 running.
to switch between pyside and PyQt you have just to change the QT_APP variable to "pyqt5"
import sys
import os
os.environ['QT_API'] = 'pyside2'
import qtpy
from qtpy.QtWidgets import QApplication, QLabel
from qtpylet import L,MapWidget
app = QApplication(sys.argv)
mapWidget = MapWidget()
map = L.map(mapWidget)
map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(map)
mapWidget.show()
app.exec_()
I am trying to include a hyperlink in the popup. For some reason this causes it not to popup. Here is the code.
`import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton
from pyqtlet2 import L, MapWidget
class MapWindow(QWidget):
marker2 = L.marker
def __init__(self):
# Setting up the widgets and layout
super().__init__()
self.mapWidget = MapWidget()
self.layout = QVBoxLayout()
self.layout.addWidget(self.mapWidget)
self.button = QPushButton("Push Me")
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.add_point)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.' + '<a href="https://www.google.com">Visit Google</a>')
self.map.addLayer(self.marker)
self.marker.click.connect(self.click_marker)
self.show()
def add_point(self):
print("added point")
def click_marker(self, event):
print("click", event)
if name == 'main':
app = QApplication(sys.argv)
widget = MapWindow()
sys.exit(app.exec_())`
There seems to be still something missing.
Normal installation produces a web/modules/leaflet_draw_414/dist containing only "images".
All code is lost.
I'm using pyqtlet2 to display information from automotive measurement files (MDF).
This error (danielhrisca/asammdf#751) was raised and I don't know what could be the cause. Can you spare some time and have a look?
Hi,
I'm trying to use your very nice library in a Jupyter Notebook.
But currently i'm stumbling over the problem, that the Map is already loaded, when i want to load the Jupyter Notebook Cell again.
Here is my Testcode:
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtCore import QCoreApplication
from pyqtlet2 import L, MapWidget
class MapWindow(QMainWindow):
def __init__(self):
super().__init__()
self.mapWidget = MapWidget()
self.setCentralWidget(self.mapWidget)
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.show()
app = QCoreApplication.instance()
if app is None: app = QApplication([])
window=MapWindow()
window.show()
app.exec_()
This error is known in the leaflet comunity:
[https://github.com/Leaflet/Leaflet/issues/3962]
[(https://stackoverflow.com/questions/19186428/refresh-leaflet-map-map-container-is-already-initialized)]
and there are different solutions for that. For example:
if (map != undefined) { map.remove(); }
But i currently don't know where to put this in the library code.
Hi, I would like to display a heatmap but Leafleat doesn't have this functionality natively.
However, a lot of plugins can be used, such as heatmap.js.
Is there any way I could use self.map.runJavaScript()
to include such a plugin and use it?
Hi,
thanks for your work.
Is is possible to get information about a marker when user click on it ?
map.clicked.connect is quiet when you click on a marker.
Thanks
Create a minimal set of unit tests which test each class and its functionality
Hello,
I am trying to using Pyqtlet to implement a map viewer, since there are lots of markers, the map viewer performance is not good. as a opportunity, I found canvasmarklayer extension, sounds this can resolve this huge markers performance issue, I try to integrated canvasmarkerlayer into pyqtlet, but i failed, when i add markers into my canvasmarkerlayer, there is nothing to displayed in the map. it confuse me a week, can you help me fix it? thanks.
you can find the canvasmarker layer js package from canvasemarklayer, i also attach it.
Leaflet-CanvasMarker-v2.1.1.zip
Hi I'm trying to run your basic example on a Ubuntu pc, but seems it's blocking somewhere in the code.
I added some prints in the mapwidget.py file and in the _loadPage function can't go over the init_loop.exec().
My program just stops waiting for something from the map.
On the other side, on Windows, it seems to work quite good.
Do you have any idea?
Thank you
Hi
is there any way for offline map with pyqtlet
when i had downloaded tiles??
Leaflet is now at version 1.9.3. I've tested it with my code, and it seems to work as a drop-in replacement.
Hi!
After install pyqtlet2 using: pip3 install pyqtlet2[PySide6]
Running the example code (shown below) seems to result in an empty/grey screen (ui elements such as zoom control and markers are shown however).
I have tried various tiles providers; however, it did not help.
import os
import sys
os.environ['QT_API'] = 'pyside6'
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from pyqtlet2 import L, MapWidget
class MapWindow(QWidget):
def __init__(self):
# Setting up the widgets and layout
super().__init__()
self.mapWidget = MapWidget()
self.layout = QVBoxLayout()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.')
self.map.addLayer(self.marker)
self.show()
if __name__ == '__main__':
app = QApplication(sys.argv)
widget = MapWindow()
sys.exit(app.exec_())
Add a known limitaions list, as well as a list of workaround which can be used.
Research and test if there is a possibility to use a local map layer and there is code needed to make this available to everyone.
Recent versions of QtWebEngine have stricter policies when it comes to accessing remote content. As it stands, tiles don't load.
The fix would be to add in the MapWidget constructor :
self._page.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls,True)
Hope that helps
I need the runJavascript method to run synchronously and I tried doing that by adding an event loop as a callback function but I get an error that runJavaScript() takes 3 positional arguments but 4 were given. Apparent, there is seems to be no overload that takes 4 arguments (callback inclusive)
Getting error in the following code .tried everything to resolve couldnt figure it out.
Error: Map.js:1092 Uncaught Error: Map container is already initialized.
import React, { Component } from "react";
import { MapContainer, TileLayer, Marker, Popup } from "react-leaflet";
import { Card, Row, Col, CardHeader, CardBody, Badge } from "reactstrap";
class Map extends Component {
render() {
return (
<MapContainer
center={[33.6844, 73.0479]}
zoom={1}
scrollWheelZoom={false}
>
<TileLayer
attribution='<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> <a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>'
url="https://api.maptiler.com/maps/basic-v2/256/{z}/{x}/{y}.png?key=Y1H1qhyFkWqTvXcK7kj"
/>
<Marker position={[33.6844, 73.0479]}>
<Popup>
A pretty CSS3 popup. <br /> Easily customizable.
</Popup>
</Marker>
</MapContainer>
</CardBody>
</Card>
</Col>
</div>
);
}
}
export default Map;
Hi, I just discovered the pyqtlet2 package. I have tried the basic example, and then try and add the fitBounds method for the map widget but it does not have any effect. I am doing something wrong?
`import os
import sys
os.environ['QT_API'] = 'pyqt5'
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from pyqtlet2 import L, MapWidget
class MapWindow(QWidget):
def init(self):
# Setting up the widgets and layout
super().init()
self.mapWidget = MapWidget()
self.layout = QVBoxLayout()
self.layout.addWidget(self.mapWidget)
self.setLayout(self.layout)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([-2.8608165094824143, -79.05583521123442], 10)
self.map.clicked.connect(lambda x: print(x))
tile_opt = {
'attribution': 'Map data: © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
'maxZoom': 10000
}
_tile = L.tileLayer('http://www.google.cn/maps/vt?lyrs=s@189&gl=cn&x={x}&y={y}&z={z}', tile_opt)
_tile.addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.')
self.map.addLayer(self.marker)
south_west = (-2.8608165094824143, -79.05583521123442)
north_east = (-2.8520920958914373, -79.03675224795658)
bounds =(south_west, north_east)
self.map.fitBounds(bounds)
self.show()
if name == 'main':
app = QApplication(sys.argv)
widget = MapWindow()
sys.exit(app.exec_())`
I am trying to make a background layer to display behind a QGrapchisView lines and plots. So the fitBounds method would make the mapwidget scene be the same as the QGrapicsView scene rect.
Thank you for any help.
i want to change cache-control :max_age :10000000
but setHttpHeader not work
and max-age is always 3600(1hour)
do u know another way for changing setHttpHeader???
import os
from PySide2.QtCore import QEventLoop, Qt, QUrl,QByteArray
from PySide2.QtWebChannel import QWebChannel
from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEnginePage, QWebEngineProfile
from PySide2.QtWebEngineCore import QWebEngineUrlRequestInterceptor, QWebEngineUrlRequestInfo
class CacheControlInterceptor(QWebEngineUrlRequestInterceptor):
def interceptRequest(self, info: QWebEngineUrlRequestInfo):
request_url = info.requestUrl().toString()
print(f"Request URL: {request_url}")
if info.resourceType() == QWebEngineUrlRequestInfo.ResourceTypeImage:
# Set custom cache control
info.setHttpHeader(QByteArray(b'Cache-Control'), QByteArray(b'public,max-age=10000000'))
class MapWidget(QWebEngineView):
"""
The MapWidget class is a QWebEngineView that houses the leaflet map.
Since it is a QWidget, it can be added to any QLayout.
"""
@property
def page(self):
return self._page
@property
def channel(self):
return self._channel
def __init__(self, use_file_absolute_path: bool = True, alternative_base_path: str = "", cache_path: str = ""):
super().__init__()
if use_file_absolute_path or len(alternative_base_path) == 0:
self.base_path = os.path.dirname(os.path.abspath(__file__))
else:
self.base_path = alternative_base_path
# Create a QWebEngineProfile and set the cache path
self._profile = QWebEngineProfile.defaultProfile()
if cache_path:
self._profile.setCachePath(cache_path)
self._page = QWebEnginePage(self._profile, self)
self.setPage(self._page)
self._channel = QWebChannel()
self._page.setWebChannel(self._channel)
# Set the interceptor
self._interceptor = CacheControlInterceptor()
self._profile.setRequestInterceptor(self._interceptor)
self._loadPage()
self.setContextMenuPolicy(Qt.NoContextMenu)
def _get_page_path(self):
return os.path.join(self.base_path, 'web', 'map.html')
def _loadPage(self):
html_path = self._get_page_path()
# QEventLoop is used to make the page loading behave synchronously
init_loop = QEventLoop()
self._page.loadFinished.connect(init_loop.quit)
self._page.load(QUrl().fromLocalFile(html_path))
init_loop.exec_()
It seems the new version 0.4.8 is unable to run the standard test script.
The unadulterated script taken from README.md dies with:
/workspace/tools/prove/venv/bin/python /workspace/tools/prove/t1.py
Traceback (most recent call last):
File "/workspace/tools/prove/t1.py", line 31, in <module>
widget = MapWindow()
File "/workspace/tools/prove/t1.py", line 19, in __init__
self.marker = L.marker([12.934056, 77.610029])
File "/workspace/tools/prove/venv/lib/python3.8/site-packages/pyqtlet2/leaflet/layer/marker/marker.py", line 14, in __init__
self.opacity = options.get('opacity', 0)
AttributeError: 'NoneType' object has no attribute 'get'
Process finished with exit code 1
FYI: I was trying to produce a minimal example of another (possible) bug: apparently trying to use "polygon with hole" syntax does not work because passing LatLon as [lat, lon]
gets translated in [(lat, lon)]
.
I can go into gory detail, but I think we should understand what went wrong with v0.4.8.
... especially so because it turns out the "other bug" is actually mine.
V0.4.7 seems to work ok.
Thanks for your patience
Mauro
Originally posted by burkeerr May 5, 2022
So far this Lib is excellent but i have hit a snag and cant seam to get around it. I want to add a point to the map by clicking a button in QT. This part works fine. But the popup never pops up for the added marker. The marker that was run during init works as expected but the added marker does not. Additionally I added a callback for the click and that works fine for the initial marker but not the added marker. Any suggestions?
`
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget, QPushButton
from pyqtlet2 import L, MapWidget
class MapWindow(QWidget):
marker2 = L.marker
def __init__(self):
# Setting up the widgets and layout
super().__init__()
self.mapWidget = MapWidget()
self.layout = QVBoxLayout()
self.layout.addWidget(self.mapWidget)
self.button = QPushButton("Push Me")
self.layout.addWidget(self.button)
self.setLayout(self.layout)
self.button.clicked.connect(self.add_point)
# Working with the maps with pyqtlet
self.map = L.map(self.mapWidget)
self.map.setView([12.97, 77.59], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([12.934056, 77.610029])
self.marker.bindPopup('Maps are a treasure.')
self.map.addLayer(self.marker)
self.marker.click.connect(self.click_marker)
self.drawControl = L.control.draw()
self.map.addControl(self.drawControl)
self.show()
def add_point(self):
self.marker2 = L.marker([12.934056, 77.650029]).addTo(self.map).bindPopup("Hi there")
self.marker2.click.connect(self.click_marker)
print("added point")
def click_marker(self, event):
print("click", event)
if name == 'main':
app = QApplication(sys.argv)
widget = MapWindow()
sys.exit(app.exec_())
`
Hi,
this is most likely some goofiness on my side and not a pyqtlet2 problem, but...
I had to fish out a program I wrote using pyqtlet2 and it flatly refuses to work.
I still have the original copy installed on a laptop and it still works there.
I need to move it to another computer as preliminary to port it to run on Win but it plain fails to work.
Even the standard example behaves in the same way:
it enters in MapWidget._loadPage()
gets into init_loop.exec_()
and never returns from there.
I am probably missing something in the new installation, but I cannot divine what.
What should I check?
TiA!
I need an additional signal for map interaction. Leaflet has a contextmenu signal (like right mouse click).
layer.py
def bindPopup(self, content, options=None):
js = '{layerName}.bindPopup("{content}"'.format(
layerName=self._layerName, content=content)
if options:
js += ', {options}'.format(self._stringifyForJs(options))
js += ')'
self.runJavaScript(js)
return self
when called marker.bindPopup(' ', {'autoPan': False})
, then throw
js += ', {options}'.format(self._stringifyForJs(options))
KeyError: 'options'
js += ', {options}'.format(options=self._stringifyForJs(options))
is ok
hello!
I use the following code to create the map
'''
self.map = L.map(self.mapWidget)
self.map.setView([50.1364092, 8.5991296], zoom)
L.tileLayer("https://{s}.tile.openstreetmap.fr/osmfr/{z}/{x}/{y}.png").addTo( self.map)
'''
But I wanted to create two maps, so I called the above code twice. But the map created by the second is gray and shows nothing.
Then I read ”evented.py“ this file。
I found that there was a problem with this " if Evented.mapWidget: return"
because Evented.mapWidget is not None,so it return.
Then before "self.map = L.map(self.mapWidget)",
add this line "pyqtlet2.leaflet.core.Evented.mapWidget = None"
Now both maps show ok.
But the first created map can't be right-clicked to add markers
Extend the usage of the parser in all location of the project where options
is then passed on to js. Have a look at the L.marker
as an example.
hello ,pyqtlet is so amazing, thanku for share it
i design a gui with pyqt5 when click on map drone go to lat and long is mouse clicked
but i have a problem
my mouse sign is openhand , when mouse click, the sign is still openhand ,but when mouse is hangover marker is finger sign
i need when click the the mouse sign convert from openhand to finger hand
is it possible ??
Thank u
hello!
I want to move the marker in map, but the "bug" happened.
I use PySide6=6.2.2 and the master of pyqtlet2, and test with the code in "README.md":
the First problem, I construct a Marker like "self.marker = L.marker([12.934056, 77.610029], {"draggable": True})", then the marker can move by the mouse, but if I do like this "self.marker = L.marker([12.934056, 77.610029]) self.marker.setDragging(True)", the marker cannot move.
the Second problem, if I construct some markers like "self.marker = L.marker([12.934056, 77.610029], {"draggable": True})" before "self.show()", then all the markers can move, but the bug happened when add new markers by right click event. in the right click event slot function, I use " L.marker([12.934056, 77.610029], {"draggable": True}).addTo(self.map)", after the new marker appeared in map, the new marker can move with the mouse, but after move end, all the markers cannot move , even the map cannot move, only map scale function can work.
if I do like "the Second problem" but without the option of "{"draggable": True}", then everything is ok.
so how can I solve the problems?
Extend the Layer
module to support leaflet Icons, which can then be used as marker!
Please consider adding something like:
diff --git a/pyqtlet2/leaflet/layer/layer.py b/pyqtlet2/leaflet/layer/layer.py
index 19179cd..478ed8e 100644
--- a/pyqtlet2/leaflet/layer/layer.py
+++ b/pyqtlet2/leaflet/layer/layer.py
@@ -40,9 +40,11 @@ class Layer(Evented):
def addTo(self, map_):
map_.addLayer(self)
+ return self
def removeFrom(self, map_):
map_.removeLayer(self)
+ return self
def bindPopup(self, content, options=None):
js = '{layerName}.bindPopup("{content}"'.format(
to allow for standard chaining like:
tag = L.polygon([c, a], {'color': color}).addTo(self.map).layerName
instead of the more verbose:
arrow = L.polygon([c, a], {'color': color})
arrow.addTo(self.map)
tag = arrow._ayerName
I can provide a proper pull request, if you want, but this is really trivial ;)
Hi all,
Since I upgraded my program from pyqtlet to pyqtlet2 I am getting a runJavaScript error like this:
TypeError: Evented.runJavaScript() missing 1 required positional argument: 'mapWidgetIndex'
As soon as I'm commenting the runJavaScript program lines, my program works fine. Could anyone please help to find out what I'm doing wrong. My QWidget class is embedded in a PyQt6 framework and is running without any others errors.
Here is my program:
import sys
from qtpy.QtWidgets import QApplication, QVBoxLayout, QWidget
from PyQt6.QtCore import *
from PyQt6.QtCore import Qt
from .mapwidget import MapWidget
from .leaflet import L
from PyQt6 import QtCore, QtWidgets, QtWebEngineWidgets, QtWebChannel
from PyQt6.QtWebEngineWidgets import *
from PyQt6.QtWebEngineWidgets import QWebEngineView
from PyQt6.QtWebEngineCore import QWebEnginePage, QWebEngineProfile, QWebEngineScript, QWebEngineSettings
class gpsMap(QWidget):
def __init__(self, parent = None):
QWidget.__init__(self, parent)
self.mapWidget = MapWidget()
self.layout = QVBoxLayout()
self.layout.addWidget(self.mapWidget)
self.profile = self.mapWidget._page.profile()
self.profile.settings().setAttribute(QWebEngineSettings.WebAttribute.LocalContentCanAccessRemoteUrls,True)
self.setLayout(self.layout)
self.map = L.map(self.mapWidget)
self.map.setView([51.5, 0], 10)
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png').addTo(self.map)
self.marker = L.marker([51.5, 0]).addTo(self.map)
self.marker = L.marker([51.6, 0],options={"opacity": 1, "draggable": 'true'})
self.map.runJavaScript('var markerIcon = L.icon({iconUrl: "https://leafletjs.com/examples/custom-icons/leaf-red.png"});')
self.map.runJavaScript(f'{self.marker.jsName}.setIcon(markerIcon);')
with open("/web/modules/leaflet_pie/Pie.js") as f:
self.map.runJavaScript(f.read())
self.map.runJavaScript("L.pie([51.7, 0], [45, 55], options={radius: 10000, labels: false}).addTo(self.map)")
self.show()
self.map.clicked.connect(lambda x: print(x))
def keyPressEvent(self, event):
if event.key() == Qt.Key.Key_Space:
self.newMarker()
def newMarker(self):
self.map.removeLayer(self.marker)
self.marker = L.marker([47.13713, 7.24608])
self.map.addLayer(self.marker)
def move(self, event):
print('move', event)
def move_end(self, event):
print('Move end', event["sender"].latLng)
def setZoomWarning(self, event):
zoom = event['zoom']
if zoom < 6:
self.label.setText('Woah buddy. You\'re flying \
pretty high. Hope you don\'t have vertigo..')
else:
self.label.setText('Yup, no worries. A fall from \
here shoudn\'t hurt... too much.')
def setWarnings(self):
self.map.getZoom(self.setZoomWarning)
Thank you yery much for your help and efforts in advance.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.