wx.ScreenDC / screenshotting problems

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

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190811183940.GA1508%40hermes.hilbert.loc.

Found this

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

but it does not offer a solution.

Karsten

···

On Sun, Aug 11, 2019 at 08:39:40PM +0200, Karsten Hilbert wrote:

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

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190811183940.GA1508%40hermes.hilbert.loc.

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

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190811184304.GB1508%40hermes.hilbert.loc.

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.

Karsten

···

On Sun, Aug 11, 2019 at 08:43:04PM +0200, Karsten Hilbert wrote:

On Sun, Aug 11, 2019 at 08:39:40PM +0200, Karsten Hilbert wrote:

> 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 ?

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

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190811185011.GA16575%40hermes.hilbert.loc.

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

···

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.

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/alpine.DEB.2.21.1908112326410.5553%40bear.techie.net.

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

···

On Sun, Aug 11, 2019 at 11:27:05PM -0400, Scott Talbert 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.

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

--
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-users+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/wxpython-users/20190812083750.GA3001%40hermes.hilbert.loc.