Comments (9)
Thanks for reporting an issue!
This would actually be difficult to implement, at least to my knowledge... I wonder if we could use the await
feature from Python 3.5 in order to implement this. The thing is we need to wait for the JavaScript front-end to compute the text measure and then return the value to the Python back-end. I've got no knowledge of these things yet but that would be awesome if you had some ideas!
from ipycanvas.
Before we get into the possibilities the idea behind this request is two-fold:
- for any graphical application that uses text which is not monospace we do not know how long a given text string will be on screen or canvas before rendering. So it would be ideal to ask the authority here, that is the browser, for the given font (incl. size, kerning, etc.) and then adapt the next drawing steps around this.
- for any longer text when we want to use line breaks in a given fixed box size all algorithms require as input the length of the current line which again from the python perspective is unknown.
I completely agree with you after looking into this a bit that the implementation is not as straightforward as hoped. From a high level view the asynchronous model is not really suited for this. Maybe that is motivation to look a bit deeper into this issue. At least a few other people ran into a similar issue:
jupyter-widgets/ipywidgets#1783
jupyter/notebook#3187
ipython/ipykernel#369
https://stackoverflow.com/questions/60002189/execute-javascript-synchronously-in-jupyter-notebook-cell
Here in particular one could imagine to circumvent this in a hackish way. Instead of
length = canvas.measure_text("abc def")
draw_box(width = length+padding)
it would be possible to do it in two steps:
guessed_length = canvas.measure_text("abc def")
draw_box(width = guessed_length+padding)
[...]
draw_other_parts()
update_box(width = canvas.measure_text_trait)
Another alternative is building a small table character
<-> width in px
. That does not take kerning into account but should be very close to the real value and a pretty good first guess.
from ipycanvas.
Thanks a lot for looking into this!
I like the alternative of creating a table, as it would allow to keep the same API as the JavaScript one. Although it would be difficult to make a complete table for all characters at all fonts and font sizes.
I wonder if using PIL to measure the text size Python side would return the same value the web canvas would return? See https://stackoverflow.com/questions/43828955/measuring-width-of-text-python-pil.
Note that pillow is already an ipycanvas dependency.
from ipycanvas.
Additional links:
https://pillow.readthedocs.io/en/stable/reference/ImageFont.html#imagefont-module
https://pillow.readthedocs.io/en/stable/reference/ImageDraw.html#PIL.ImageDraw.PIL.ImageDraw.ImageDraw.textsize
from ipycanvas.
I wonder if we could use the
await
feature from Python 3.5 in order to implement this. The thing is we need to wait for the JavaScript front-end to compute the text measure and then return the value to the Python back-end. I've got no knowledge of these things yet but that would be awesome if you had some ideas!
It should be possible to transparently create and return Future
objects to the caller that can be awaited. And expose the result as part of the future result.
I was experimenting a bit with this lately in jtpio/ipylab@9c43df7 (not merged yet):
import asyncio
from ipylab import JupyterFrontEnd
app = JupyterFrontEnd()
async def sequence():
await app.ready()
for i in range(4):
result = await app.commands.execute('filebrowser:toggle-main')
await asyncio.sleep(1)
asyncio.create_task(sequence());
However it's not possible to await
at the top-level (blocking), but the workaround is to create a new asyncio.Task
.
from ipycanvas.
It should be possible to transparently create and return
Future
objects to the caller that can be awaited. And expose the result as part of the future result.
And maybe even hooking into the widgets message ids?
from ipycanvas.
Any update, so far ?
from ipycanvas.
Nope, unfortunately. But any help is welcome :)
from ipycanvas.
I have a suggestion for a fairly simple workaround.
Use Pillow (which you say is already a dependency) to render text to an image in Python (server-side). Then send the image to the client. Yes, that's terribly inefficient, but it can be implemented easily and it has the additional advantage that the user can pick a specific font and be sure it will be available for rendering. If your render text in the Browser (client-side), that's harder (or impossible?) to do. Plus, there is no back-and-forth communication involved that isn't already happening when we draw images onto a canvas. The downside is that we won't get the stroke/fill/shadow functionality of the canvas for text.
For those of us who need this to be efficient, you could offer an API function that lets us pre-render text (for use in an animation, for example). So we call the currently imaginary ipycanvas.prerender_text(text, font, size), which might take a moment, but will create an image, send it to the client and return a handle for it to the python caller. Then a call to a currently imaginary canvas.show_prerendered_text(handle, x ,y) function will tell javascript to render the image onto the target canvas.
Most of this can be implemented as a python module that is completely separate from ipycanvas itself, but it might be more efficient if it's part of ipycanvas.
from ipycanvas.
Related Issues (20)
- How to get the new path HOT 7
- add width, height support to put_image_data HOT 1
- How to add transform to Path2D HOT 6
- voila fails to load ipycanvas.js from localhost HOT 2
- how to drag an image around on the canvas? HOT 2
- bundle text and shape into one object HOT 4
- ipycanvas does not work with JupyterLab 4 HOT 4
- debug_output only works if calling display(canvas) HOT 1
- ipycanvas 0.13.1 does not work with google colab. 0.13 works. HOT 1
- Take a screenshot of a drawing/canvas and save it? HOT 2
- current canvas out of sync after page reload
- Commands JSON content mangled HOT 3
- Error displaying widget: model not found HOT 1
- is there no call to remove a shape? HOT 1
- bug on canvas.clear() HOT 1
- any way to pause animation? HOT 2
- feature request for mouse drag HOT 2
- Using Path2D for stroke
- Error with fresh jupyterlab 3 install - "Failed to load model class" / "Error: No version of module ipycanvas is registered" HOT 3
- The docs link to github needs updating HOT 5
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 ipycanvas.