【问题标题】:Multiple screenshots using ScreenDC, wxPython使用 ScreenDC、wxPython 的多个屏幕截图
【发布时间】:2017-03-23 18:53:25
【问题描述】:

我在 wxPython Phoenix 中遇到了 ScreenDC 的问题。

我的工具应该在一段时间内拍摄多个屏幕截图。但每当我使用 ScreenDC 抓取屏幕截图并将其保存为 PNG 时,它只能在第一次正常工作。在接下来的所有时间里,它只保存与第一个相同的图像。要获得新图像,我必须重新启动程序,这不是我的选择。我想每当我打电话给wx.ScreenDC() 时,它都会得到与第一次相同的图像。

Ubuntu 16.04、wxPython 3.0.3 gtk3、python 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,而不是 Bitmap)。很奇怪,我会在他们的存储库中报告这个问题。

【问题讨论】:

标签: python wxpython wxwidgets


【解决方案1】:

我无法在 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()

编辑:正如提问者所指出的,上述方法中可能存在线程安全问题。下面的东西对你有什么作用(在 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()

【讨论】:

  • 我尝试在没有 sleep() 的情况下工作,但结果是一样的。此外,对于 wxPython GUI,应该非常小心地使用线程,请在此处阅读:wiki.wxpython.org/Non-Blocking%20Gui。在这种情况下,bmp.SaveFile(...) 应该从主线程调用,否则你会得到多线程 C 错误。这似乎只在 Linux 上存在,在 Windows 上没有这些错误。我最终改用 python 2,这很容易。
  • 那么,您介意发布回溯吗?并且主循环需要在主线程中。只要保持线程安全,从线程调用 wx 并非不可能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多