wx.ScreenDC / screenshotting problems

1,082 views
Skip to first unread message

Karsten Hilbert

unread,
Aug 11, 2019, 2:39:45 PM8/11/19
to wxPython-users
I am using code based on Andrea Gavana's screenshotting code
to take, well, screenshots of widgets of my application.

It worked fine on wxp3 but does not quite on wxp4.

The gist of the code is to

get the full screen coordinates of the widget to screenshot

get a wx.ScreenDC

get a wx.MemoryDC

tie the memory DC to a bitmap

Blit() the appopriate region of the screen DC into
the memory DC

save the bitmap

The first time this is applied to my maximized application
the proper image is created.

However, when I change what is shown inside my maximized
application and then re-screenshot the image will show the
exact same screenshot as when the wx.ScreenDC was first
created and screenshotted.

As if the wx.ScreenDC is a global singleton or else the same
memory region happens to be reused. Print()ing the DC's in
question shows that they are at different addresses :-/

I do take care the SelectObject(wx.NullBitmap) etc after
screenshotting but nothing seems to help. Here is the code I
am running:

#-------------------------------------------------------------------
def save_screenshot_to_file(filename=None, window=None):

assert (isinstance(window, wx.Window)), '<window> must be (sub)class of wx.Window'

print('widget to snap:', window)
widget_on_screen_rect = window.GetScreenRect()
print('getscreenrect:', widget_on_screen_rect)
x2snap_on_screen = max(0, widget_on_screen_rect.x)
y2snap_on_screen = max(0, widget_on_screen_rect.y)
print('sane (x,y) on screen to snap from:', x2snap_on_screen, y2snap_on_screen)

widget_rect = window.GetRect()
print('getrect:', widget_rect)
width2snap = widget_rect.width
height2snap = widget_rect.height
print('(w,h) to snap:', width2snap, height2snap)

# adjust for window decoration on Linux
if sys.platform == 'linux':
print('adjusting for widget decorations')
client_x, client_y = window.ClientToScreen((0, 0))
print('client2screen 0x0 coords:', client_x, client_y)
border_width_x = max(0, widget_rect.x)
border_width_y = max(0, widget_rect.y)
print('border (x,y):', border_width_x, border_width_y)
x2snap_on_screen = max(0, x2snap_on_screen - border_width_x)
y2snap_on_screen = max(0, y2snap_on_screen - border_width_y)

print('possibly adjusted (x,y) on screen to snap from:', x2snap_on_screen, y2snap_on_screen)
# get image from screen
screen_dc = wx.ScreenDC()
print('screen DC:', screen_dc)
widget_dc = wx.WindowDC(window)
print('widget DC:', widget_dc)
target_dc = wx.MemoryDC()
print('target DC:', target_dc)
wxbmp = wx.EmptyBitmap(width2snap, height2snap)
print(wxbmp)
target_dc.SelectObject(wxbmp)
target_dc.Blit ( # copy into this memory DC ...
0, 0, # ... to here in the memory DC (= target) ...
width2snap, height2snap, # ... that much ...
screen_dc, # ... from the screen DC ...
x2snap_on_screen, y2snap_on_screen # ... starting here
)
target_dc.SelectObject(wx.NullBitmap) # prevent ghosts ?
wxbmp.SaveFile(filename, wx.BITMAP_TYPE_PNG)
# cleanup
target_dc.Destroy()
del target_dc
screen_dc.Destroy()
del screen_dc
del widget_dc
del wxbmp
return filename

#-------------------------------------------------------------------

Anything obvious I am missing here ?

Thanks,
Karsten
--
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B

Karsten Hilbert

unread,
Aug 11, 2019, 2:43:07 PM8/11/19
to wxPython-users
Found this

https://cmsdk.com/python/multiple-screenshots-using-screendc-wxpython.html

but it does not offer a solution.

Karsten
> --
> You received this message because you are subscribed to the Google Groups "wxPython-users" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to wxpython-user...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190811183940.GA1508%40hermes.hilbert.loc.

Karsten Hilbert

unread,
Aug 11, 2019, 2:50:14 PM8/11/19
to wxPython-users
On Sun, Aug 11, 2019 at 08:43:04PM +0200, Karsten Hilbert wrote:

> Found this
>
> https://cmsdk.com/python/multiple-screenshots-using-screendc-wxpython.html

https://github.com/wxWidgets/Phoenix/issues/259

Seems an open issue without a solution.

Screenshotting from a wx.WindowDC() instead does not readily
let one include window decorations.

Scott Talbert

unread,
Aug 11, 2019, 11:27:10 PM8/11/19
to wxPython-users
On Sun, 11 Aug 2019, Karsten Hilbert wrote:

>> Found this
>>
>> https://cmsdk.com/python/multiple-screenshots-using-screendc-wxpython.html
>
> https://github.com/wxWidgets/Phoenix/issues/259
>
> Seems an open issue without a solution.
>
> Screenshotting from a wx.WindowDC() instead does not readily
> let one include window decorations.

Wonder why Robin closed it, then. Probably should be reopened.

Karsten Hilbert

unread,
Aug 12, 2019, 4:37:56 AM8/12/19
to wxPython-users
It should. It was probably closed because someone remarked
"possibly working answer on stackoverflow".

Which "answer", however, is not to the point:

- it does not at all touch the issue (ScreenDC
contains the same image over and over)

- it concerns itself with delayed execution
which does not play a role in the issue

- it doesn't get the basics right (no .SelectObject()
neither before nor after accessing the MemoryDC)

There was no feedback from OP so it may have been considered
"closable" on the basis of "cannot replicate".

However, from my report we know it can be replicated.

Karsten
Reply all
Reply to author
Forward
0 new messages