2017-03-23 4 views
1

wxPython Phoenix의 ScreenDC에 문제가 발생했습니다.ScreenDC, wxPython을 사용하는 여러 스크린 샷

내 도구는 일정 기간 동안 여러 개의 스크린 샷을 찍을 예정입니다. 그러나 ScreenDC를 사용하여 스크린 샷을 캡처하고 PNG에 저장하면 처음에만 제대로 작동합니다. 다음의 모든 시간은 첫 번째 이미지와 동일한 이미지를 저장합니다. 새로운 이미지를 얻으려면 필자의 경우 옵션이 아닌 프로그램을 다시 시작해야합니다. 나는 wx.ScreenDC()라고 부를 때마다 처음으로 같은 이미지를 얻는다 고 생각합니다.

우분투 16.04, wxPython을 3.0.3 gtk3, 파이썬 3.6

내가 사용하는 코드 :

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) 
    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() 

어쩌면 메모리에서 첫 번째 이미지를 청소하는 방법이있다.

유일한 해결책은 별도의 프로세스를 실행하고 wx.App를 정의한 다음 함수를 수행하는 것입니다. 그러나 그것은 내 프로그램을위한 선택 사항이 아닙니다.

감사합니다.

UPD : wxPython Phoenix의 문제 일 것 같습니다. 이것을 wxPython Classic에서 실행하면 모든 것이 정상적으로 작동합니다 (비트 맵이 아닌 EmptyBitmap 만 사용). 이상한, 나는 그들의 저장소 에서이 문제를보고합니다.

+0

내가) (수면없이 작업 시도에 [Github에서 (https://github.com/wxWidgets/Phoenix/issues/259) – nepix32

답변

0

Phoenix 또는 Classic (Windows)에서 문제를 재현하지 못했습니다. 일어날 수있는 일은 sleep 블록 wxPython 이벤트 루프를 차단하는 것입니다. 어쨌든 긴 스레드를 별도의 스레드에 넣는 것이 좋은 스타일 일 것입니다. 그것은 고통스럽지 않습니다, 아래를보십시오.

from threading import Thread 

... 

if __name__ == '__main__': 
    app = wx.App() 
    def payload(): 
     take_screenshot() 
     sleep(3) 
     take_screenshot() 
     sleep(3) 
     take_screenshot() 
     sleep(3) 
     take_screenshot() 
    thrd = Thread(target=payload) 
    thrd.start() 

편집 : asker가 지적했듯이 위의 접근 방식에서는 스레드 안전성에 문제가있을 수 있습니다. 아래의 작업은 어떻게 작동합니까 (Windows의 Phoenix 및 Classic에서 테스트)?

from __future__ import print_function 

import wx 
from datetime import datetime 
from time import sleep 

IS_PHOENIX = True if 'phoenix' in wx.version() else False 

if IS_PHOENIX: 
    EmptyBitmap = lambda *args, **kwds: wx.Bitmap(*args, **kwds) 
else: 
    EmptyBitmap = lambda *args, **kwds: wx.EmptyBitmap(*args, **kwds) 

def take_screenshot(): 
    screen = wx.ScreenDC() 
    size = screen.GetSize() 
    width = size[0] 
    height = size[1] 
    bmp = EmptyBitmap(width, height) 
    mem = wx.MemoryDC(bmp) 
    mem.Blit(0, 0, width, height, screen, 0, 0) 
    bmp.SaveFile(str(datetime.now().second) + '.png', wx.BITMAP_TYPE_PNG) 

MAXPICS = 4 

class testfrm(wx.Frame): 
    def __init__(self, *args, **kwds): 
     wx.Frame.__init__(self, *args, **kwds) 
     self.tmr = wx.Timer(self, -1) 
     self.countpics = 0 
     self.Bind(wx.EVT_TIMER, self.ontimer, self.tmr) 
     self.ontimer(None) 

    def ontimer(self, evt): 
     if self.countpics <=MAXPICS: 
      self.tmr.Start(3000, wx.TIMER_ONE_SHOT) 
      take_screenshot() 
      self.countpics += 1 
     else: 
      self.Close() 


if __name__ == '__main__': 
    app = wx.App() 
    frm = testfrm(None, -1, wx.version()) 
    app.MainLoop() 
+0

보기,하지만 결과는 동일했다. 또한 스레드는 wxPython GUI에 대해 매우 신중하게 사용해야합니다 (https://wiki.wxpython.org/Non-Blocking%20Gui). 이 경우'bmp.SaveFile (...)'이 메인 쓰레드에서 호출되어야한다. 그렇지 않으면 멀티 쓰레딩 C 에러가 발생한다. 이 문제는 Linux에서만 발생하는 것으로 보이며 Windows에서 이러한 오류가 발생하지 않았습니다. 파이썬 2로 바뀌 었습니다. – proggeo

+0

그래서, 추적을 게시 하시겠습니까? 메인 루프는 메인 스레드에 있어야합니다. 상황이 스레드로부터 안전하게 유지되는 한 스레드에서 wx를 호출하는 것은 불가능하지 않습니다. – nepix32