I want to add icons to buttons etc. in my applications.
This can easily be done using bitmaps (e.g., PNG images).
However these don’t scale. This is a problem when deploying an application to users who have widely varying screen sizes and DPIs.
I’ve tried using a custom ArtProvider 256x256 PNGs and dynamically scaling to 16, 20, 24, etc., pixels in proportion to the DPI (although wxPython seems to report DPI incorrectly on Linux), but the results are poor.
Ideally I’d like to be able to use SVG images and then dynamically draw them as PNGs of the appropriate size (and cache them once drawn). But I can’t see any SVG rendering class in wxPython, only SVGFileDC which outputs SVG.
Surely other wxPython users have encountered (and solved) this problem?
(By comparison, Qt allows the use of SVG images directly for icons and scales them appropriately for you.)
There still isn’t SVG support in wxWidgets, however it looks like better HiDPI cross-platform support may be coming in 4.1.x. I haven’t investigated fully yet but if I understand correctly it involves selecting differently sized images based on the scaling factor of the display.
There is already some support for this on OSX, although if I remember correctly it requires the images to be in the Resources folder of the application bundle, or something like that, so it’s not very easy to use from a non-bundled python application. The discussions about this on wx-dev have been about making things work better and consistently on all the main platforms.
I’ve been playing around with this for the past few weeks.
All the commonly used SVG features are supported, and for those that aren’t, (like text) loading the file into an editor and changing things to simple paths will probably take care of it. The rendering to a wx.GraphicsContext is not quite perfect on all platforms, but it’s very usable and probably close enough to perfect everywhere except the GDI+ backend. The rasterizing to a wx.Bitmap does work very well everywhere.
It’s possible it could eventually grow into something like that, but for now the focus is just adding the ability to get a bitmap from an SVG, and it’s expected it will be used with the existing widget APIs.
But when I run it it fails on Windows and Linux. Here’s the error on Linux (same error on Windows):
Traceback (most recent call last):
File "/home/mark/app/myapp/ArtProvider.py", line 16, in CreateBitmap
return svg.ConvertToScaledBitmap(size)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 144, in ConvertToScaledBitmap
return self.ConvertToBitmap(scale=scale, width=size.width, height=size.height)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 115, in ConvertToBitmap
bmp = wx.Bitmap.FromBufferRGBA(width, height, buf)
RuntimeError: Failed to gain raw access to bitmap data.
No, I ran the demo and double-clicked to add a new svg and added my icon.svg and it showed up perfectly. I then changed the filename in my ArtProvider to one of the images:
Since you are using the GTK port, is it possible that you are trying to fetch the image from the art provider before the application is fully started up? (Before MainLoop is called.) if so, are you able to defer it until just after the main loop starts?
If that doesn’t seem to be the problem then I’m out of ideas and will need a small runnable sample to be able to investigate further.
Hi Robin,
Attached is Test1.py 28 LOC plus icon.svg and Test2.py 114 LOC including the SVG embedded.
Both fail with the error using Python 3.8 + wxPython 4.1.0 gtk3 wxWidgets 3.1.4.Test1.py (713 Bytes) Test2.py (3.6 KB) icon.zip (1.4 KB) (had to put icon.svg in a .zip).
Error for Test1.py:
Traceback (most recent call last):
File "Test1.py", line 23, in CreateBitmap
return svg.ConvertToScaledBitmap(size)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 144, in ConvertToScaledBitmap
return self.ConvertToBitmap(scale=scale, width=size.width, height=size.height)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 115, in ConvertToBitmap
bmp = wx.Bitmap.FromBufferRGBA(width, height, buf)
RuntimeError: Failed to gain raw access to bitmap data.
Error for Test2.py:
Traceback (most recent call last):
File "Test2.py", line 23, in CreateBitmap
return svg.ConvertToScaledBitmap(size)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 144, in ConvertToScaledBitmap
return self.ConvertToBitmap(scale=scale, width=size.width, height=size.height)
File "/home/mark/opt/py38/lib/python3.8/site-packages/wx/svg/__init__.py", line 115, in ConvertToBitmap
bmp = wx.Bitmap.FromBufferRGBA(width, height, buf)
RuntimeError: Failed to gain raw access to bitmap data.
By default the art provider will pass wx.DefaultSize to the CreateBitmap method. That value is wx.Size(-1, -1) so the bitmap created in ConvertToScaledBitmap is invalid, and that is why the “Failed to gain raw access to bitmap data” exception happens.
So you should pass the desired size to wx.ArtProvider.GetIcon or add some code like