Example of a non-scaling scrolling image view?

95 views
Skip to first unread message

Mark

unread,
Jun 19, 2019, 3:55:42 AM6/19/19
to wxPython-users
I want a widget that shows PNG images.

The widget will vary in size depending on the user sizing the window it is in but I don't want the image it shows to be scaled. Iinstead I want the image to be shown at its actual size occupying the top-left if it is smaller than the widget or with scrollbars appearing if it is larger than the widget. The images the widget will be given will vary in size, so it needs to adapt whenever it is given a new image.

Does anyone have or can direct me to an example widget that has this kind of behaviour?

Robin Dunn

unread,
Jun 19, 2019, 9:43:27 PM6/19/19
to wxPython-users
There are likely samples of this all over, but it's fairly straight forward.  I would approach it like this:
  1. Derive a new class from wx.Window
  2. In the __init__ set a bitmap attribute to wx.NullBitmap, and bind the EVT_PAINT event to a handler method, and set the background style to wx.BG_STYLE_PAINT.
  3. Give it a method to set the bitmap attribute, and have it call self.Refresh after the attribute is set.
  4. In the EVT_PAINT handler do these steps:
    1. Create a wx.AutoBufferedPaintDC
    2. Set the dc's background brush (dc.SetBackground) to whatever you want the window to look like when there is no bitmap, or in the areas that the image does not cover.
    3. Call dc.Clear()
    4. if the bitmap attribute's IsOk() method returns True, call dc.DrawBitmap to draw it.
--
Robin

Mark

unread,
Jun 20, 2019, 5:14:06 AM6/20/19
to wxPython-users
Thanks for that. I got it to work fine without scrollbars (once I'd set the BackgroundStyle), but my attempts to get scrollbars working have failed.
If you run the attached and resize the window to be smaller than the image on Gtk the scrollbars appear and don't work. On Windows the scrollbars appear and do work, but the drawing gets all messed up. This is frustrating since I feel as though I'm really close!
imageview.py

Robin Dunn

unread,
Jun 20, 2019, 11:53:58 AM6/20/19
to wxPython-users
On Thursday, June 20, 2019 at 2:14:06 AM UTC-7, Mark wrote:
Thanks for that. I got it to work fine without scrollbars (once I'd set the BackgroundStyle), but my attempts to get scrollbars working have failed.
If you run the attached and resize the window to be smaller than the image on Gtk the scrollbars appear and don't work. On Windows the scrollbars appear and do work, but the drawing gets all messed up. This is frustrating since I feel as though I'm really close!


Oops, I somehow missed the "Scrolling" in the first message. There are a few ways to set things up to scroll, some more flexible but complex, some more simple. Your attempt is the former.

The main thing you are missing is adjusting the window to the scrolled offset when painting. You can do that with a call to self.PrepareDC(dc) in your on_paint method. To simplify even further, your entire update_scrollbars can be eliminated if you just set the virtual size to be the bitmap's size whenever the bitmap changes, and scrolling can be enabled by calling SetScrollRate. The ScrolledCanvas will take care of everything else.  See attached.

--
Robin
imageview.py

Mark

unread,
Jun 20, 2019, 2:43:26 PM6/20/19
to wxPython-users
Thanks Robin, that works perfectly on both Gtk and Windows!

Out of curiosity, is it OK to replace `if bitmap.IsOk():` with `if bitmap:`?

Also, is there any reason you wrote `self.SetVirtualSize(bitmap.Size)` rather than `self.VirtualSize = bitmap.Size`?
I'm tending to use property getters/setters wherever they are available but don't really know what the recommended style is.

Tim Roberts

unread,
Jun 20, 2019, 3:59:14 PM6/20/19
to wxpytho...@googlegroups.com
Mark wrote:
>
> Out of curiosity, is it OK to replace `if bitmap.IsOk():` with `if
> bitmap:`?

No.  "if self.bitmap:" is always going to succeed.  The "if" statement
in Python only checks for a few values of truthiness: the value None,
the value False, or an empty sequence (string, list, tuple, set,
dictionary) will evaluate false.  Everything else, including a bitmap
object bound to wxNullBitmap, will evaluate to true.

--
Tim Roberts, ti...@probo.com
Providenza & Boekelheide, Inc.


Robin Dunn

unread,
Jun 20, 2019, 5:03:36 PM6/20/19
to wxPython-users
On Thursday, June 20, 2019 at 12:59:14 PM UTC-7, Tim Roberts wrote:
Mark wrote:
>
> Out of curiosity, is it OK to replace `if bitmap.IsOk():` with `if
> bitmap:`?

No.  "if self.bitmap:" is always going to succeed.  The "if" statement
in Python only checks for a few values of truthiness: the value None,
the value False, or an empty sequence (string, list, tuple, set,
dictionary) will evaluate false.  Everything else, including a bitmap
object bound to wxNullBitmap, will evaluate to true.

Actually, wxPython adds __bool__ and __nonzero__ methods to the wx.Bitmap class that return the value of IsOk(), so yes, it will work.

>>> wx.NullBitmap.IsOk()
False
>>> print('ok' if wx.NullBitmap else 'not ok')
not ok
>>> bool(wx.NullBitmap)
False
>>> 
>>> 
>>> bmp = wx.Bitmap(10,10)
>>> bmp.IsOk()
True
>>> print('ok' if bmp else 'not ok')
ok
>>> bool(bmp)
True

--
Robin



Reply all
Reply to author
Forward
0 new messages