PyQt4: take screenshot of web pages using QWebKit - python

I am using the below code to take screenshot from a webpage. But the problem is I am getting blank screenshot for certain websites. Obviously the reason is the page size is not fitting. Can some one please help me to fix this code so that it works for all webpages.
import sys
import time
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
class Screenshot(QWebView):
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
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32)
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
s = Screenshot()
s.capture('https://docs.python.org/2/library/', 'website3.png')
I have tried to modify like below from a similar thread:
PyQt: QImage() returns a 'Null'-Image. I have made a change according to the post but cant make it work.
import sys
import time
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
class Screenshot(QWebView):
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
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32)
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
frame = self.page().mainFrame()
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)
self._loaded = False
def _loadFinished(self, result):
self._loaded = True
s = Screenshot()
s.capture('https://stackoverflow.com/','website4.png')

Related

Use python to screenshot notepad

How can I use the window handle to screenshot a running instance of Notepad? I already figured out how to successfully screenshot a widget within the python dialog itself. I have also figured out how to get the handle of the running notepad window. I'm stuck on trying to capture the screenshot of the window using the handle.
import os, sys
from PySide import QtGui, QtCore
import ctypes
class GuiCaptureWindow(QtGui.QMainWindow):
def __init__(self, parent=None):
super(GuiCaptureWindow, self).__init__(parent)
self.resize(250, 100)
self.setWindowTitle('GUI Capture')
# console
self.ui_capture = QtGui.QPushButton('Capture')
main_layout = QtGui.QGridLayout()
main_layout.addWidget(self.ui_capture)
main_widget = QtGui.QWidget()
main_widget.setLayout(main_layout)
self.setCentralWidget(main_widget)
# signals
self.ui_capture.clicked.connect(self.capture)
def getRelativeFrameGeometry(self, widget):
g = widget.geometry()
fg = widget.frameGeometry()
return fg.translated(-g.left(),-g.top())
def screenCaptureWidget(self, widget, filename, fileformat='.png'):
rfg = self.getRelativeFrameGeometry(widget)
pixmap = QtGui.QPixmap.grabWindow(widget.winId(),
rfg.left(), rfg.top(),
rfg.width(), rfg.height())
filepath = os.path.abspath(filename + fileformat)
pixmap.save(filepath)
os.system("start " + filepath)
def capture(self):
self.collect_window_titles()
self.screenCaptureWidget(self.ui_capture, 'test')
def collect_window_titles(self):
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
apps = []
def foreach_window(hwnd, lParam):
if IsWindowVisible(hwnd):
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
if buff.value:
apps.append({'title': buff.value, 'handle': hwnd})
return True
EnumWindows(EnumWindowsProc(foreach_window), 0)
for app in apps:
print app
if app['title'] == 'Untitled - Notepad':
print 'CAPTURE'
return
def main():
app = QtGui.QApplication(sys.argv)
ex = GuiCaptureWindow()
ex.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

Create a jpg or png image of a web page with python

I am newbie, I want to create a jpg or png image of a web page whith PyQt4, so that you understand me for example something like this.
This script works perfect:
import sys
import time
import os
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
width= int(530)
height=int(1060)
class Screenshot(QWebView):
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()
time.sleep(2)
# set to webpage size
frame = self.page().mainFrame()
self.page().setViewportSize(QSize(width, height,))
# render image
image = QImage(self.page().viewportSize(), QImage.Format_ARGB32)
painter = QPainter(image)
frame.render(painter)
painter.end()
print ('Guardada'), 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
filename = "capture"
s = Screenshot()
s.capture('http://www.filmaffinity.com/es/main.html', filename+(time.strftime("-%H-%M-%S"))+".png")
This captures me from above the page 530 px width x
1060px height, that's fine, but I want the capture start from below the web and more extended, I mean a certain area of the web,
such as this image
How can I modify the script?
Thanks

How to see a file download progress without the GUI freezing (python 3.4, pyQt5) using QThread

So I'm trying to make a simple file downloader in Python 3.4.2 and PyQt5
QThreads seems to be the way but there's no tutorials online or examples that I could understand for PyQt5. All I could find was Qt5 Reference for C/C++ and bunch of PyQt4 tutorials that don't work for PyQt5 and Python 3
Here's the GUI screenshot: http://i.imgur.com/KGjqRRK.png
And here's my code:
#!usr/bin/env python3
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from string import Template
import urllib.request
import sys
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
lblUrl= QLabel("File URL:")
self.txtURL = QLineEdit()
self.bttDL = QPushButton("&Download")
self.pbar = QProgressBar()
self.pbar.setMinimum(0)
buttonLayout1 = QVBoxLayout()
buttonLayout1.addWidget(lblUrl)
buttonLayout1.addWidget(self.txtURL)
buttonLayout1.addWidget(self.bttDL)
buttonLayout1.addWidget(self.pbar)
self.bttDL.clicked.connect(self.bttPush)
mainLayout = QGridLayout()
mainLayout.addLayout(buttonLayout1, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("pySFD")
def bttPush(self):
# check if the download is already running or just disable the button
# while it's running
url = self.txtURL.text()
if url == "":
QMessageBox.information(self, "Empty URL",
"Please enter the URL of the file you want to download.")
return
else:
filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.'))
filename = filename[:-6]
filename = filename.split("('",maxsplit=1)[1]
self.dlThread = downloaderThread()
self.dlThread.connect(dlThread.run)
self.dlThread.start()
self.dlThread.emit(url)
class downloaderThread(QThread):
def __init__(self):
QThread.__init__(self)
def __del__(self):
self.wait()
def run(self, dlLink):
while dlLink.length == 0:
QThread.sleep(1)
pass
def report(block_count, block_size, total_size):
if block_count == 0:
self.pbar.setValue(0)
if (block_count * block_size) == total_size:
QMessageBox.information(self,"Done!","Download finished!")
return
self.pbar.setValue(self.pbar.value() + block_size)
urllib.request.urlretrieve(dlLink, filename, reporthook=report)
self.terminate()
if __name__ == '__main__':
app = QApplication(sys.argv)
screen = Form()
screen.show()
sys.exit(app.exec_())
I've tried a lot of ways and it just doesn't seem to work.
How do I make the progress bar (self.pbar) show the download progress in real time?
PS. This downloader is on my GitHub: https://github.com/dKatara/pySFD
Fixed it, it's not done yet, but threading works great!
Most recent version is on my GitHub here: https://github.com/dKatara/pySFD
#!usr/bin/env python3
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import urllib.request
import sys
import threading
dlThread = 0
hWindow = 0
fProgressCounter = 0.0
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
global hWindow
hWindow = self
lblUrl = QLabel("File URL:")
self.txtURL = QLineEdit()
self.bttDL = QPushButton("&Download")
self.pbar = QProgressBar()
self.pbar.setMinimum(0)
self.pbar.setMaximum(100)
buttonLayout1 = QVBoxLayout()
buttonLayout1.addWidget(lblUrl)
buttonLayout1.addWidget(self.txtURL)
buttonLayout1.addWidget(self.bttDL)
buttonLayout1.addWidget(self.pbar)
self.bttDL.clicked.connect(self.bttPush)
mainLayout = QGridLayout()
mainLayout.addLayout(buttonLayout1, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("pySFD")
def bttPush(self):
global dlThread
hSignals = sigHandling()
hSignals.dlProgress_update.connect(hSignals.pbar_incrementer)
hSignals.dlProgress_done.connect(hSignals.dlDone)
url = self.txtURL.text()
if url == "":
QMessageBox.information(self, "Empty URL",
"Please enter the URL of the file you want to download.")
return
else:
filename = str(QFileDialog.getSaveFileName(self, 'Choose the download location and file name', '.')) ## DETECT A CANCEL
filename = filename[:-6]
filename = filename.split("('",maxsplit=1)[1]
self.bttDL.setEnabled(False)
dlThread = threading.Thread(target=hSignals.runDL,args=(url, filename))
dlThread.start()
return
def pbarIncValue(self, val):
global fProgressCounter
#print("pbarIncValue({0})\nfProgressCounter={1}".format(val,fProgressCounter))
if self.pbar.value() >= 100:
self.dlProgress_done.emit()
return
if fProgressCounter > 1.0: # FIX
self.pbar.setValue(self.pbar.value() + 1)
fProgressCounter -= 1.0
fProgressCounter += val
else:
fProgressCounter += val
class sigHandling(QObject):
dlProgress_update = pyqtSignal(float)
dlProgress_done = pyqtSignal()
#pyqtSlot(float)
def pbar_incrementer(self, val):
hWindow.pbarIncValue(val)
#pyqtSlot()
def dlDone(self):
print("in dlDone")
hWindow.pbar.setValue(100)
hWindow.bttDL.setEnabled(True)
def runDL(self, dlLink, filename):
#print("in run")
global dlThread, hWindow
def report(block_count, block_size, total_size):
if block_count == 0:
#print("block_count == 0")
self.dlProgress_update.emit(0)
if (block_count * block_size) == total_size:
self.dlProgress_done.emit()
incAmount = float((100*block_size) / total_size)
#print("BS={0} TS={1} incAmount={2}".format(block_size,total_size,incAmount))
self.dlProgress_update.emit(incAmount)
urllib.request.urlretrieve(dlLink, filename, reporthook=report)
#print("emit dlProgress_done")
self.dlProgress_done.emit()
#print("about to leave dlThread")
pass
if __name__ == '__main__':
app = QApplication(sys.argv)
screen = Form()
screen.show()
sys.exit(app.exec_())

1 QPushButton control 2 action from 2 different class

This is a pyside GUI, I have created 2 panel in 2 different .py files, main.py and sub.py each panel will display a we browser 'QWebView'. Currently when user press the button on main.py it will redirect the user to a page e.g "www.google" and user will have to click the button on sub.py to be redirected to e.g"www.facebook.com" they work as a indipendent function.
I would like to ask is there a way to link both together where user press the button on main.py and both webbrower will change together?
Yes, you can have multiple items triggered by the same connection.
QObject::connect(myButton, SIGNAL(clicked()),
this, SLOT(launchGoogleSiteOnBrowserA());
QObject::connect(myButton, SIGNAL(clicked()),
pointerToOtherClass, SLOT(launchFacebookSiteOnBrowserB());
http://qt-project.org/doc/qt-4.8/signalsandslots.html
EDIT: Following some another answer about using signals and slots in PyQt...
https://stackoverflow.com/a/7618282/999943
Here is a way to do it in PyQt:
widget.pyw
from PyQt4 import QtCore, QtGui
from mybrowser import Browser
class Widget(QtGui.QWidget):
def __init__(self):
super(Widget, self).__init__()
self.myButton = QtGui.QPushButton('Open Facebook and Google')
self.myHLayout = QtGui.QHBoxLayout()
self.myVLayout = QtGui.QVBoxLayout()
self.myVLayout.addWidget(self.myButton)
url = QtCore.QUrl('http://www.yahoo.com')
self.browserLHS = Browser(url)
self.browserRHS = Browser(url)
self.myHLayout.addWidget(self.browserLHS)
self.myHLayout.addWidget(self.browserRHS)
QtCore.QObject.connect(self.myButton, QtCore.SIGNAL("clicked()"), self.changePageOnBothBrowsers )
self.myVLayout.addLayout(self.myHLayout)
self.setLayout(self.myVLayout)
def changePageOnBothBrowsers(self):
self.browserLHS.load(QtCore.QUrl.fromUserInput('google.com'))
self.browserRHS.load(QtCore.QUrl.fromUserInput('facebook.com'))
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
widget = Widget()
widget.show()
sys.exit(app.exec_())
mybrowser.pyw
from PyQt4 import QtCore, QtGui, QtNetwork, QtWebKit
import jquery_rc
class Browser(QtWebKit.QWebView):
def __init__(self, url):
super(Browser, self).__init__()
self.progress = 0
fd = QtCore.QFile(":/jquery.min.js")
if fd.open(QtCore.QIODevice.ReadOnly | QtCore.QFile.Text):
self.jQuery = QtCore.QTextStream(fd).readAll()
fd.close()
else:
self.jQuery = ''
QtNetwork.QNetworkProxyFactory.setUseSystemConfiguration(True)
self.load(url)
self.loadFinished.connect(self.adjustLocation)
self.titleChanged.connect(self.adjustTitle)
self.loadProgress.connect(self.setProgress)
self.loadFinished.connect(self.finishLoading)
self.locationEdit = QtGui.QLineEdit(self)
self.locationEdit.setSizePolicy(QtGui.QSizePolicy.Expanding,
self.locationEdit.sizePolicy().verticalPolicy())
self.locationEdit.returnPressed.connect(self.changeLocation)
def adjustLocation(self):
self.locationEdit.setText(self.url().toString())
def changeLocation(self):
url = QtCore.QUrl.fromUserInput(self.locationEdit.text())
self.load(url)
self.setFocus()
def adjustTitle(self):
if 0 < self.progress < 100:
self.setWindowTitle("%s (%s%%)" % (self.title(), self.progress))
else:
self.setWindowTitle(self.title())
def setProgress(self, p):
self.progress = p
self.adjustTitle()
def finishLoading(self):
self.progress = 100
self.adjustTitle()
self.page().mainFrame().evaluateJavaScript(self.jQuery)
#if __name__ == '__main__':
#
# import sys
#
# app = QtGui.QApplication(sys.argv)
#
# if len(sys.argv) > 1:
# url = QtCore.QUrl(sys.argv[1])
# else:
# url = QtCore.QUrl('http://www.google.com/ncr')
#
# browser = Browser(url)
# browser.show()
#
# sys.exit(app.exec_())
Hope that helps.

How to use qtwebkit in python threads?

I'm trying to parse webpages generated by js with qtwebkit, I found an example of how to get page source:
import sys
from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtWebKit import *
class Render(QWebPage):
def __init__(self, url):
self.app = QApplication(sys.argv)
QWebPage.__init__(self)
self.loadFinished.connect(self._loadFinished)
self.mainFrame().load(QUrl(url))
self.app.exec_()
def _loadFinished(self, result):
self.frame = self.mainFrame()
self.app.quit()
url = 'http://www.thesite.gov/search'
r = Render(url)
html = r.frame.toHtml()
But i don't know how to make it work in threads.
So, how to do this and if it's not possible - is there another fast way to get wepages generated by js?
Given QT's async nature, the QtWebkit methods are non-blocking as well, so there is no point running them in threads. You can start them parallelly like this:
from functools import partial
from PySide.QtCore import QUrl
from PySide.QtGui import QApplication
from PySide.QtWebKit import QWebView, QWebSettings
TARGET_URLS = (
'http://stackoverflow.com',
'http://github.com',
'http://bitbucket.org',
'http://news.ycombinator.com',
'http://slashdot.org',
'http://www.reddit.com',
'http://www.dzone.com',
'http://www.ideone.com',
'http://jsfiddle.net',
)
class Crawler(object):
def __init__(self, app):
self.app = app
self.results = dict()
self.browsers = dict()
def _load_finished(self, browser_id, ok):
print ok, browser_id
web_view, _flag = self.browsers[browser_id]
self.browsers[browser_id] = (web_view, True)
frame = web_view.page().mainFrame()
self.results[frame.url()] = frame.toHtml()
web_view.loadFinished.disconnect()
web_view.stop()
if all([closed for bid, closed in self.browsers.values()]):
print 'all finished'
self.app.quit()
def start(self, urls):
for browser_id, url in enumerate(urls):
web_view = QWebView()
web_view.settings().setAttribute(QWebSettings.AutoLoadImages,
False)
loaded = partial(self._load_finished, browser_id)
web_view.loadFinished.connect(loaded)
web_view.load(QUrl(url))
self.browsers[browser_id] = (web_view, False)
if __name__ == '__main__':
app = QApplication([])
crawler = Crawler(app)
crawler.start(TARGET_URLS)
app.exec_()
print 'got:', crawler.results.keys()

Categories