Comments (11)
Thanks a lot for reporting this issue, @backbord!
I just bisected it and identified commit a2544c1 (PR #2867), which was a collaboration with @afullerx and part of release 1.4.23. Somehow the way we process the outbox queue has an effect on the Leaflet element.
I'll try to investigate further. But if anyone else has an idea, I'm all ears.
from nicegui.
I was able to consistently reproduce the bug using 1.4.21. (Windows 10, Python 3.12). While the changes made in #2867 may be causing it to be triggered more consistently for more users, it seems as though it is not the root cause.
from nicegui.
Got it! Here is what happens:
- The "flyTo" transition takes about 1 second to move to Berlin. It ends with a "map-zoomend" and a "map-moveend" event being sent to the server.
- The "map-zoomend" is handled first. It causes an update which is sent to the client. But because no "map-moveend" event has been processed yet, the update still contains the old center, which causes the client to move back to Munich.
- Now the "map-moveend" event is handled. The updated center is sent to the client, which cause it to move to Berlin.
from nicegui.
Thank you for looking into it and the fix!
from nicegui.
Ah, actually that kind of makes sense:
When introducing asyncio events in PR #2867, we implicitly removed a short delay of 0.01s between loop cycles. Now the outbox reacts immediately when new messages are added to the queue, which could have changed the behavior of ui.leaflet
. When adding an asyncio.sleep(0.01)
after the event has been awaited
Line 61 in 614ea49
the flicker disappears.
Why does the problem already exist on Windows before PR #2867? As we noticed in #2482, Windows tends to ignore delays shorter than 0.016s, so the implicit delay of 0.01s is ineffective.
Now I want to find out the exact order of messages that leads to the endless loop. Maybe it's a Leaflet-specific problem. Otherwise we need to rethink the outbox.
from nicegui.
You nailed it there @falkoschindler. Here is the sequence of messages/updates after the button is clicked:
Message: ('78ed9408-c2ec-4c5c-95e8-79f3d503e2e7', 'run_javascript', {'code': 'return runMethod(5, "run_map_method", ["flyTo",[52.5,13.4],9,{"duration":1.0}])'})
After the "flyTo" operation is completed, the following updates are issued (only the _props
attribute of the queued Leaflet
object was recorded):
{'resource_path': '/_nicegui/1.4.20/resources/763203f93f18a3f1f5d14f74197580e4', 'center': (48.1, 11.6), 'zoom': 9, 'options': {}, 'draw_control': False}
{'resource_path': '/_nicegui/1.4.20/resources/763203f93f18a3f1f5d14f74197580e4', 'center': [52.5, 13.4], 'zoom': 9, 'options': {}, 'draw_control': False}
{'resource_path': '/_nicegui/1.4.20/resources/763203f93f18a3f1f5d14f74197580e4', 'center': [48.1, 11.6], 'zoom': 9, 'options': {}, 'draw_control': False}
{'resource_path': '/_nicegui/1.4.20/resources/763203f93f18a3f1f5d14f74197580e4', 'center': [52.5, 13.4], 'zoom': 9, 'options': {}, 'draw_control': False}
[...repeated endlessly]
An incorrect update is issued with the original Munich coordinates, quickly followed by the one with the correct Berlin coordinates. When a delay is present, the first update is overwritten before it is emitted, and everything functions properly. Without the delay, both updates are issued in quick succession, causing the leaflet to enter an endless loop navigating between them.
from nicegui.
Thanks, @afullerx!
By the way, I thought I might be able to reproduce the problem with plain HTML/JavaScript, but this code works perfectly fine:
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body>
<div id="map" style="height: 400px"></div>
<button id="flyToBerlin">Berlin</button>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
<script>
const map = L.map("map").setView([48.1, 11.6], 10);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png").addTo(map);
document.getElementById("flyToBerlin").onclick = () => map.flyTo([52.5, 13.4], 9, { duration: 1.0 });
map.on("moveend", console.log);
</script>
</body>
</html>
from nicegui.
I think outbox.py is ok and ui.leaflet
is doing something fishy. Let's add
print(f'{time.time():.6f}', 'outbox enqueue_update', getattr(element, 'center', None))
and
print(f'{time.time():.6f}', 'outbox enqueue_message', message_type, data)
to outbox.py and run this slightly modified example:
count = {'value': 0}
def tick():
count['value'] += 1
if count['value'] == 10:
sys.exit(0) # interrupt endless loop
m = ui.leaflet(center=(48.1, 11.6), zoom=10)
m.on('map-moveend', lambda e: (print(f'{time.time():.6f}', 'on map-moveend:', e.args['center']), tick()))
ui.button('Berlin', on_click=lambda: (print('click'), m.run_map_method('flyTo', [52.5, 13.4], 9, {'duration': 1.0})))
Output:
click
1715678352.542828 outbox enqueue_message run_javascript {'code': 'return runMethod(4, "run_map_method", ["flyTo",[52.5,13.4],9,{"duration":1.0}])'}
1715678353.564281 outbox enqueue_update [48.1, 11.6]
1715678353.564758 outbox enqueue_update [52.5, 13.4]
1715678353.565157 on map-moveend: [52.5, 13.4]
1715678353.569320 outbox enqueue_update [48.1, 11.6]
1715678353.572813 on map-moveend: [48.1, 11.6]
And the oscillation begins.
But why is there an update with the old center [48.1, 11.6]
one second after "flyTo" has been started? Is this the root cause?
from nicegui.
I just noticed that the "Wait for Initialization" demo is flickering as well. It's probably the same problem.
from nicegui.
Hi again!
While the patch fixes the behavior in most cases, sometimes the map still begins to flicker.
I recon this was expected as with a unfortunate ordering of events (#3035 (comment)), this is a question of timing and batching those events.
For my use case, I've come up with this workaround, which is far from perfect.
In most cases (on my machine), it manages to stop the flickering.
def avoid_flickering(leaflet: ui.leaflet) -> None:
# This is a workaround for https://github.com/zauberzeug/nicegui/issues/3035
# as it hasn't been solved completely.
last_centers: deque[tuple[float, float]] = deque(maxlen=2)
last_move_at = 0.0
def _avoid_flickering(event: events.GenericEventArguments) -> None:
nonlocal last_move_at
lat, lon = event.args["center"]
center = (lat, lon)
now = time.monotonic()
time_since_last_move = now - last_move_at
# print(f"{time_since_last_move:.4f} {last_centers} {center}")
if (
time_since_last_move < 0.05
and len(last_centers) > 1
and last_centers[1] != center == last_centers[0]
):
# try to stop flickering
leaflet.run_map_method("off", "moveend")
leaflet.set_center(last_centers[1])
leaflet.run_map_method("on", "moveend")
print(
f"Flickering detected. "
f"Attempting to stop by setting center to {last_centers[-1]}."
)
last_centers.append(center)
last_move_at = now
leaflet.on("map-moveend", _avoid_flickering)
Please feel free to suggest improvements. :)
Hope this helps!
Thanks
from nicegui.
@backbord, you could also try increasing the sleep durations in_handle_moveend()
and _handle_zoomend()
:
nicegui/nicegui/elements/leaflet.py
Lines 85 to 91 in 63c2024
It would be useful to know if there's a duration that works reliably for you. With the current sleep of only 20ms, there's a lot of room to play with.
from nicegui.
Related Issues (20)
- Bump plotly.js to 2.32.0 version supporting zindex HOT 4
- Lifespan state is not passed when a FastAPI app mount a Nicegui app HOT 3
- plotly is missing from docker image HOT 2
- How to get the value of a cell slot within a table?
- how can nicegui upload all files and subdirectory in an directory?
- auto-reload make pyinstaller build failed HOT 2
- `ui.codemirror` does not work via NiceGUI On Air
- Audio with hashtags in filenames are not loaded HOT 1
- Native mode raises 'webview' has not attribute 'settings' HOT 1
- Pytest: recursive dependency in fixtures HOT 2
- `ui.number`: change event argument differs from `value` property HOT 1
- Confusing desync with blocking calls HOT 15
- while running smoothly after few minutes this issue occurred..please help HOT 1
- Breaking change trapping await javascript in version 1.4.26 HOT 12
- Packaging with nicegui-package makes process hang HOT 12
- Plotly charts are not responsive HOT 2
- Deploy nice gui on Aws beanstalk similar to django and flask
- Too short default time-out for notification element HOT 1
- JS exception due to wrong variable in download function HOT 1
- Some of Tailwind's minimum height classes get ignored HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from nicegui.