Closed proggeo closed 7 years ago
Has been asked (with possible working answer) on Stackoverflow.
I do not think this issue is solved: http://wxpython-users.1045709.n5.nabble.com/wx-ScreenDC-screenshotting-problems-td5730158.html Please re-check. Thanks, Karsten
I see this also with Classic (but built against GTK 3), so I think this is probably rather something to do with the GTK 3 implementation of wxScreenDC and nothing wxPython specific. Probably needs a C++ reproducer and ticket opened on trac.wxwidgets.org.
Yep, there are also similar problems with OSX.
My understanding is that due to the additional layers between the applications and the display for automatic buffering and compositing, that we don't have the same level of access to the display's image buffer that we had before with the older systems, and so we simply can not fetch what is displayed on the screen via the wx.ScreenDC API in order to Blit it into a bitmap. I think I saw somebody say that you can think of wx.ScreenDC being a write-only interface now.
That said, on OSX you can use wx.ScreenDC.GetAsBitmap to get an image of the screen. It's not implemented for GTK3 unfortunately.
On Mon, Aug 12, 2019 at 06:29:06PM -0700, Robin Dunn wrote:
Yep, there are also similar problems with OSX.
My understanding is that due to the additional layers between the applications and the display for automatic buffering and compositing, that we don't have the same level of access to the display's image buffer that we had before with the older systems, and so we simply can not fetch what is displayed on the screen via the wx.ScreenDC API in order to Blit it into a bitmap.
I feared as much. Put another way: there's no way to fully (including window decorations) screenshot an application from within wxPhoenix ?
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
If you are willing to try other packages, then pyscreenshot might be a way:
https://github.com/ponty/pyscreenshot
Since apparently they have done it, I don’t see why it shouldn’t be possible in wx. Also, doesn’t wxWidgets have its own screenshot tool for the documentation:
Andrea.
On Tue, 13 Aug 2019 at 08.27, ncqgm notifications@github.com wrote:
On Mon, Aug 12, 2019 at 06:29:06PM -0700, Robin Dunn wrote:
Yep, there are also similar problems with OSX.
My understanding is that due to the additional layers between the applications and the display for automatic buffering and compositing, that we don't have the same level of access to the display's image buffer that we had before with the older systems, and so we simply can not fetch what is displayed on the screen via the wx.ScreenDC API in order to Blit it into a bitmap.
I feared as much. Put another way: there's no way to fully (including window decorations) screenshot an application from within wxPhoenix ?
Karsten
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/wxWidgets/Phoenix/issues/259?email_source=notifications&email_token=ACESNIN7I46O2RZ6VLAJSI3QEJH63A5CNFSM4DFBOMDKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD4EVSCA#issuecomment-520706312, or mute the thread https://github.com/notifications/unsubscribe-auth/ACESNILULMFGA7W467NKPKTQEJH63ANCNFSM4DFBOMDA .
On Tue, Aug 13, 2019 at 06:43:02AM +0000, Andrea Gavana wrote:
Also, doesn’t wxWidgets have its own screenshot tool for the documentation:
I saw that before but looked again:
AFAICS they do exactly what I do, except for a
memory_DC.Clear()
after setting the bitmap object on the memory_DC:
bmp = wx.Bitmap()
memory_DC.SelectObj(bmp)
memory_DC.Clear() # <==
memory_DC.Blit(..., screen_DC, ...)
memory_DC.SelectObj(wx.NullBitmap)
bmp.SaveFile()
I can't see how this should make a difference but I'll give it a try. That sequence (w/o the .Clear()) certainly does not work for me.
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
On Tue, Aug 13, 2019 at 06:43:02AM +0000, Andrea Gavana wrote:
If you are willing to try other packages, then pyscreenshot might be a way:
They are doing it the brutal way: Creating a new wx.App for each screenshot.
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
On Mon, Aug 12, 2019 at 06:29:06PM -0700, Robin Dunn wrote:
My understanding is that due to the additional layers between the applications and the display for automatic buffering and compositing, that we don't have the same level of access to the display's image buffer that we had before with the older systems, and so we simply can not fetch what is displayed on the screen via the wx.ScreenDC API in order to Blit it into a bitmap. I think I saw somebody say that you can think of wx.ScreenDC being a write-only interface now.
I am not so sure anymore that I understand that line of thought:
Upon the first instantiation of wx.ScreenDC() it works just right - the ScreenDC contains a copy of the desktop at that time.
Subsequent, new, instantiations of the same class will not.
Why would one have access to the desktop content the first time around but not later on ?
We are not talking live interaction with the desktop here.
Why should it be technically possible to read once but not more than once ?
Surely that sounds like the first instantiation is cached somewhere and merely returned later on ?
GPG 40BE 5B0E C98E 1713 AFA6 5BC0 3BEA AC80 7D4F C89B
Faced the same problem on Ubuntu 19.10, wxPython 4.0.6 gtk3 (phoenix) wxWidgets 3.0.4, python 3.7.5. And it seems I found workaround:
Just insert screen.Blit(0, 0, width, height, screen, 0, 0)
after mem.Blit(0, 0, width, height, screen, 0, 0)
. So the working (for my setup) version of the code is:
import wx
from datetime import datetime
from time import sleep
def take_screenshot():
screen = wx.ScreenDC()
size = screen.GetSize()
width = size[0]
height = size[1]
bmp = wx.Bitmap(width, height)
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, width, height, screen, 0, 0)
screen.Blit(0, 0, width, height, screen, 0, 0) # <-- this line makes multiple screenshots work
bmp.SaveFile(str(datetime.now()) + '.png', wx.BITMAP_TYPE_PNG)
if __name__ == '__main__':
app = wx.App()
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
sleep(3)
take_screenshot()
upd:
It is better to use wx.OR
logical function in screen-on-screen blitting (screen.Blit(0, 0, width, height, screen, 0, 0, wx.OR)
), because oherwise in Windows it paints the screen white.
Am Thu, Mar 18, 2021 at 02:56:51AM -0700 schrieb The-o:
Faced the same problem on Ubuntu 19.10, wxPython 4.0.6 gtk3 (phoenix) wxWidgets 3.0.4, python 3.7.5. And it seems I found workaround:
Just insert
screen.Blit(0, 0, width, height, screen, 0, 0)
aftermem.Blit(0, 0, width, height, screen, 0, 0)
.
I can confirm this works for me. It perhaps "updates" a globally cached (?) Screen DC from layers further down, closer to the hardware ?
Karsten
It perhaps "updates" a globally cached (?) Screen DC from layers further down, closer to the hardware ?
I don't really know how and why it works. I believe it is something to do with wxWidgets and not with wxPython itself. I just saw that thare was a different if-branch in the latest wxWidgets GTK DC implementation when the source and the destination DCs were the same (https://github.com/wxWidgets/wxWidgets/blob/v3.1.4/src/gtk/dc.cpp#L152). Then the idea of trying to repaint screen with its own content came to me. And it surprisingly worked.
But the confusing fact is that there is no such an if-branch in wxWidgets v3.0.4 I have (https://github.com/wxWidgets/wxWidgets/blob/v3.0.4/src/gtk/dc.cpp#L138). And I'm not good at cairo, so the mystery of why the workaround works still remains for me.
If you take a screenshot with
wx.ScreenDC()
for few times, then every time you will get the same image: the screenshot at the moment whenwx.ScreenDC()
was called for the first time. The only solution found is to run it every time in separate Process with its ownwx.App()
inside.However, if you do this on wxPython Classic, it works just fine. Sample code down here: