Multiple screenshots using ScreenDC, wxPython - python

I ran into a problem with ScreenDC in wxPython Phoenix.
My tool is supposed to take multiple screenshots with some period. But whenever I use ScreenDC to grab a screenshot and save it to PNG it works well only for the first time. All the following times it just saves the same image as the first one. To get a new image, I have to restart the program, which is not an option in my case. I guess that whenever I call wx.ScreenDC() it gets the same image as the first time.
Ubuntu 16.04, wxPython 3.0.3 gtk3, python 3.6
The code I used:
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()
Maybe there is the way to clean that first image from memory.
The only solution I found is to run a separate process, define wx.App inside and then to perform the function. However, that is not an option for my program.
Thanks.
UPD: It seems to be some issue of wxPython Phoenix. If you run this on wxPython Classic, everything works fine(just use EmptyBitmap, not Bitmap). Weird, I will report this issue in their repository.

I was not able to reproduce your issue in Phoenix or Classic (on Windows). I suppose what could happen is that sleep blocks wxPython event loop. It would be good style to put long-running things in a separate thread anyway. It is painless, see below.
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()
EDIT: As the asker pointed out, there may be issues with thread-safety in the approach above. How does the thing work below for you (tested on Phoenix and Classic on Windows)?
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()

Related

Tkinter program works fine in IDE (Visual Studio) but when using pyinstaller to compile to .exe threading does not work same like it does in IDE

When I run my python project in my IDE the GUI and everything is responsive and works great. But when I run as .exe my threading components don't work like they do in IDE. The program's goal is to grab a live feed via RTSP and using opencv to display the images. This is done in its own thread here.
import time
import threading
import cv2
import PIL.Image
"""TODO: add docstring"""
class VideoCapture:
def __init__(self, xmlDict=None, width=None, height=None, fps=None):
"""TODO: add docstring"""
self.xmlDict = xmlDict
self.width = width
self.height = height
self.fps = int(self.xmlDict['FPS'])
self.running = False
# Open the video source
self.vid = cv2.VideoCapture(self.xmlDict['IpAddress'])
if not self.vid.isOpened():
raise ValueError("[MyVideoCapture] Unable to open video source", xmlDict['IpAddress'])
# Get video source width and height
if not self.width:
self.width = int(self.vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # convert float to int
if not self.height:
self.height = int(self.vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # convert float to int
if not self.fps:
self.fps = int(self.vid.get(cv2.CAP_PROP_FPS)) # convert float to int
# default value at start
self.ret = False
self.frame = None
self.convert_color = cv2.COLOR_BGR2RGB
#self.convert_color = cv2.COLOR_BGR2GRAY
self.convert_pillow = True
# start thread
self.running = True
self.thread = threading.Thread(target=self.process)
self.thread.start()
def process(self):
"""TODO: add docstring"""
while self.running:
ret, frame = self.vid.read()
if ret:
# process image
frame = cv2.resize(frame, (self.width, self.height))
# it has to record before converting colors
if self.convert_pillow:
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = PIL.Image.fromarray(frame)
else:
print('[MyVideoCapture] stream end:', self.video_source)
# TODO: reopen stream
self.running = False
if self.recording:
self.stop_recording()
break
# assign new frame
self.ret = ret
self.frame = frame
# sleep for next frame
#if self.fps != "FULL":
# time.sleep(1/int(self.fps))
I have a button setup called start that infers an image every 2 seconds and prints out the label and confidence. When I do this in .exe the live feed and GUI freeze while inference is being made but when I use program in IDE it does not freeze. Here is the code that does this.
#Button to start inference
self.btn_snapshot = tk.Button(self.btnFrame,width = 10,height = 2, text="Start", command=lambda:threading.Thread(target = self.snapshot).start())
self.btn_snapshot.grid(row = 1,column = 0)
#snapshot function
def snapshot(self):
self.recording = True
while self.recording:
filename = self.vid.snapshot()
result = self.predictImage(filename)
output = self.calculatePassFail(result)
if self.manager:
self.manager.onClick(output)
else:
print('something')
time.sleep(2)
The other two methods that the snapshot function calls are predictImage and calculatePassFail.
def predictImage(self,imageName):
onnxModel = ImageModel.load(self.xmlDict['ModelPath'])
result = onnxModel.predict_from_file(imageName)
return result
def calculatePassFail(self,result):
calcResult = result.labels[0]
self.labelName = calcResult[0]
self.imgScore = calcResult[1]*100
return f"{self.labelName} with score{self.imgScore}"
So I found a fix around this, not sure if its a proper fix but it works. So for some reason when I use pyinstaller to create .exe and there is a console window I have the issue but when I use the flag --noconsole when using pyinstaller to create w/out console the issue goes away and my inference on images acts in its own thread like it does in my IDE. Not sure why it does but it works I guess.

Video player in gtk using opencv gets stuck

I am trying to simply play a video in gtk environment using opencv code in python. In order to achieve it I made a glade file that contains a toplevel window, a file menu, a drawing area and a file chooser dialog. When user select a file, code starts a thread that calls function VideoPlayerDA that starts reading video and after every frame it generates a queue_draw signal to display frame in drawing area. The problem however is that after few frames the whole UI freezes and becomes unresponsive, video gets stuck.
Tools: I am using Gtk version 3.22.11, python 3.5.3, OpenCV version 3.3.0 on debian stretch.
PS: cv2.waitkey also seems to be not working.
import cv2
import time
import threading
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, Gdk, GdkPixbuf
GObject.threads_init()
mymutex = threading.Lock()
dimg = GdkPixbuf.Pixbuf.new_from_file('test.jpg')
def VideoPlayerDA(filename,drawing_area):
global dimg,dimg_available
cap = cv2.VideoCapture(filename)
while(cap.isOpened()):
mymutex.acquire()
ret, img = cap.read()
if img is not None:
boxAllocation = drawing_area.get_allocation()
img = cv2.resize(img, (boxAllocation.width,\
boxAllocation.height))
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # opencv by default load BGR colorspace. Gtk supports RGB hance the conversion
dimg = GdkPixbuf.Pixbuf.new_from_data(img.tostring(),
GdkPixbuf.Colorspace.RGB,False,8,
img.shape[1],
img.shape[0],
img.shape[2]*img.shape[1],None,None)
#time.sleep(0.03)
drawing_area.queue_draw()
mymutex.release()
time.sleep(0.03)
#if ((cv2.waitKey(30) & 0xFF) == ord('q')):
# break
else:
mymutex.release()
break
print('end of file')
class video_player_gui:
def on_main_window_destroy(self,object):
Gtk.main_quit()
def on_open_activate(self,widget):
response = self.file_chooser.run()
if response == 0:
self.filename = self.file_chooser.get_filename()
thread = threading.Thread(target = VideoPlayerDA, args=(self.filename, self.drawing_area,))
thread.daemon = True
thread.start()
self.file_chooser.hide()
else:
pass
def on_drawing_area_draw(self,widget,cr):
global dimg
Gdk.cairo_set_source_pixbuf(cr, dimg.copy(), 0, 0)
cr.paint()
def __init__(self):
self.gladefile = '/home/nouman/Development/Glade/P2/OpenCv_integration_test.glade'
self.builder = Gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.builder.connect_signals(self)
self.main_window = self.builder.get_object("main_window")
self.file_chooser = self.builder.get_object("file_chooser")
self.drawing_area = self.builder.get_object("drawing_area")
self.main_window.show()
if __name__ == "__main__":
main = video_player_gui()
Gtk.main()
I found a solution for now for anyone who will be getting same problem. I was using a global variable dimg without thread synchronization. Synchronizing threads before use of variable solved the problem. Edit on_drawing_area_draw as following will solve the issue
def on_drawing_area_draw(self,widget,cr):
global dimg
mymutex.acquire()
Gdk.cairo_set_source_pixbuf(cr, dimg.copy(), 0, 0)
cr.paint()
mymutex.release()

cef python - Freeze when resizing

Today I am trying to develop an UI using cefpython which allows me to embed a web browser and interacts with it with javascript bindings.
I'm using it to develop on Windows platform.
For this purpose, I am using the "multi_threaded_message_loop" flag which allows me to gain in performance.
I'm also using wxpython on python 3 to embed it.
The problem is when I resize my window, the use of WindowUtils.OnSize() freezes my app. 99% of the time, it happens when the browser is loading (but it also happens when it's done (rarely)).
Here is a sample code to reproduce :
import platform
import sys
import wx
from cefpython3 import cefpython
WindowUtils = cefpython.WindowUtils()
WIDTH = 800
HEIGHT = 600
import os
class MainFrame(wx.Frame):
browser = None
mainPanel = None
def createMainBrowser(self):
self.browser = self.createBrowser(self.mainPanel)
def createBrowser(self, parent):
browser = cefpython.CreateBrowserSync(
self.getWindowInfo(parent),
browserSettings={},
navigateUrl='http://www.google.com'
)
return browser
def getWindowInfo(self, parent):
windowInfo = cefpython.WindowInfo()
windowInfo.SetAsChild(parent.GetHandle(), [0, 0, WIDTH, HEIGHT])
return windowInfo
def __init__(self):
wx.Frame.__init__(
self, parent=None, id=wx.ID_ANY, title='wx', size=(WIDTH, HEIGHT)
)
self.mainPanel = wx.Panel(self)
self.mainPanel.SetBackgroundColour(wx.GREEN)
cefpython.PostTask(cefpython.TID_UI, self.createMainBrowser)
self.mainPanel.Bind(wx.EVT_SIZE, self.OnSize)
def OnSize(self, _):
if not self.browser:
return
WindowUtils.OnSize(self.mainPanel.GetHandle(), 0, 0, 0)
self.browser.NotifyMoveOrResizeStarted()
class App(wx.App):
def OnInit(self):
frame = MainFrame()
frame.Show()
return True
if __name__ == '__main__':
sys.excepthook = cefpython.ExceptHook # To shutdown all CEF processes on error
cefpython.Initialize({
"locales_dir_path": cefpython.GetModuleDirectory() + "/locales",
"browser_subprocess_path": cefpython.GetModuleDirectory() + "/subprocess",
"auto_zooming": "system_dpi",
"multi_threaded_message_loop": True,
})
app = App(False)
app.MainLoop()
cefpython.Shutdown()
Thank you a lot for your help !
Alann
Problem solved !
Instead of using
def OnSize(self, _):
if not self.browser:
return
WindowUtils.OnSize(self.mainPanel.GetHandle(), 0, 0, 0)
self.browser.NotifyMoveOrResizeStarted()
I use
def OnSize(self, sizeEvent):
if not self.browser:
return
w = sizeEvent.GetSize().GetWidth()
h = sizeEvent.GetSize().GetHeight()
win32gui.SetWindowPos(self.browser.GetWindowHandle(), 0, 0, 0, w, h, 0)
self.browser.NotifyMoveOrResizeStarted()
I don't know if this is because I'm on windows 10 but maybe WindowsUtils needs to be updated !

PyQt: QImage() returns a 'Null'-Image

I need to take screenshots of websites for a project of mine. As development language I use Python and to take the Screenshots I use Webkit from PyQt. The script below is the code used to take capture the websites (it's partially modified but most of it is still equal to the original from webscraping.com).
Now my problem is the following:
Most of the time it works without any problems, but, from time to time it happens that the following exception.
QPainter::begin: Paint device returned engine == 0, type: QPainter::setRenderHint: Painter must be active to set rendering hints
QPainter::setBrush: Painter not active
QPainter::pen: Painter not active
QPainter::setPen: Painter not active
QPainter::end: Painter not active, aborted
I've already tracked down the problem to the creation of the image
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32)
The QImage returned by this line is sometime Null - I checked this using the .isNull()-method of QImage.
According to the Qt documentation this happens if there isn't enough memory to allocate a new QImage, but I still have way enough free memory.
This beahaviour occured while running on windows and also on linux, so it should not depend on some os depending stuff, I think. I'm new to Qt and PyQt, so I hope someone can help me.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
import sys
import time
# ############################################################# #
# This script is based on the following script: #
# https://webscraping.com/blog/Webpage-screenshots-with-webkit/ #
# ############################################################# #
class Screenshot(QWebView):
_instance = None
#staticmethod
def get_instance():
# TODO: Add a lock around the if including the creation!
if Screenshot._instance is None:
Screenshot._instance = Screenshot()
return Screenshot._instance
def __init__(self):
self.app = QApplication(sys.argv)
QWebView.__init__(self)
self._loaded = False
self.loadFinished.connect(self._loadFinished)
def capture(self, url, output_file):
self.load(QUrl(url))
self.wait_load()
# set to webpage size
frame = self.page().mainFrame()
self.page().setViewportSize(frame.contentsSize())
# render image
# creating the image. Here it happens that QImage returns a 'Null'-Image
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32)
# check if there's no image allocated
if image.isNull():
print 'image.isNull() is True'
if image is None:
print 'image is None is True'
painter = QPainter(image)
frame.render(painter)
painter.end()
print 'saving', output_file
image.save(output_file)
def wait_load(self, delay=0):
# process app events until page loaded
while not self._loaded:
self.app.processEvents()
time.sleep(delay)
self._loaded = False
def _loadFinished(self, result):
self._loaded = True
if __name__ == '__main__':
# a simple way to get the exceptions is to try to create multiple screenshots
sc = Screenshot()
for i in range(0, 25):
sc.capture('http://google.de', str(i) + '.png')
for i in range(25, 50):
sc.capture('http://de.wikipedia.org', str(i) + '.png')
Ok. I've tracked the problem a bit further down. It seems like the contentsSize of the QWebPages mainFrame is sometimes (0, 0) when it is read to create the QImage.
frame = self.page().mainFrame()
self.page().setViewportSize(frame.contentsSize()) # frame.contentsSize() = (0, 0)
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32) # so we're creating here an Image with Width: 0 and Height: 0
So basically the QImage seems to be Null, because the size it's created with is (0, 0).
This problem can be solved by checking wether the mainFrames contentsSize is (0, 0) or not. If it is (0, 0) it's needed to process outstanding events on the QApplication until the new contentsSize is set. I'm doing this now with the following piece of code:
if frame.contentsSize().width() == 0 or frame.contentsSize().height() == 0:
print 'ContentsSize = (w: {}, h: {})'.format(frame.contentsSize().width(), frame.contentsSize().height())
count = 0 # used so we're not starting an infinite loop
while (frame.contentsSize().width() == 0 or frame.contentsSize().height() == 0) and count < 5:
count += 1
self.app.processEvents()
time.sleep(1)

Running raw_input on a pygtk application

I need to make a graphical application that reads data from the console to update some widgets, this will have to do with 2 threads, one for the GUI and one for the console. The problem is that raw_input function does not work and also freezes the application. Here is the code.
import pygtk
pygtk.require('2.0')
import gtk
import gobject
from time import sleep
import sys
import threading
class Worker (threading.Thread):
def __init__(self, app):
threading.Thread.__init__(self)
self.app = app
def run(self):
text = raw_input("Enter some text: ") #It freezes the application
#text = "Hola" #It Works
self.app.writeMessage(text, False)
class Application:
def __init__(self, title, xPos, yPos):
gtk.threads_init()
self.win = gtk.Window()
screen = self.win.get_screen()
screenW = screen.get_width()
screenH = screen.get_height()
windowW = int(screenW * 0.5)
windowH = int(screenH * 0.25)
if type(xPos) is float:
xPos = int(screenW * xPos)
if type(yPos) is float:
yPos = int(screenH * yPos)
self.messageArea = gtk.TextView()
self.scroll = gtk.ScrolledWindow()
self.scroll.add(self.messageArea)
self.win.set_size_request(windowW, windowH)
self.win.set_title(title)
self.win.add(self.scroll)
self.win.show_all()
self.win.move(xPos, yPos)
self.win.connect("destroy", gtk.mainquit)
def doOperation(self, function, *args, **kw):
def idle_func():
try:
gtk.threads_enter()
function(*args, **kw)
return False
finally:
gtk.threads_leave()
gobject.idle_add(idle_func)
def sleeper():
time.sleep(.001)
return 1 # don't forget this otherwise the timeout will be removed
def mainloop(self):
#Trick for running threads and pygtk on win32 enviroment
if sys.platform == 'win32':
gtk.timeout_add(400, self.sleeper)
gtk.threads_enter()
gtk.mainloop()
gtk.threads_leave()
def writeMessage(self, message, isMainThread):
if isMainThread:
buf = self.messageArea.get_buffer()
end_iter = buf.get_end_iter()
buf.insert(end_iter, message)
else:
self.doOperation(self.writeMessage, message, True)
if __name__ == "__main__":
app = Application("Hello", 0, 0)
worker = Worker(app)
app.doOperation(worker.start)
app.mainloop()
Curiously the code only works if you run it in eclipse pydev, but it doesn't is the intention, I must run it from console. So this is the question, how to execute raw_input function and GUI on separate threads?
take a look at this.It explains about absence of event loop support in python and you can disable pygtk event loop after importing it.

Categories

Resources