Python QtWebKit 2nd link not downloaded - qt

I am trying to download the content of both the links.The first link works properly and the content is downloaded as a html file.But the content of the 2 nd link(a text file) is not downloaded.Please help me out.
Here is my code :
from PySide.QtCore import *
from PySide.QtGui import *
from PySide.QtWebKit import *
import sys
import codecs
class Downloader(QObject):
# To be emitted when every items are downloaded
done = Signal()
def __init__(self, urlList, parent = None):
super(Downloader, self).__init__(parent)
self.urlList = urlList
self.counter = 0
# As you probably don't need to display the page
# you can use QWebPage instead of QWebView
self.page = QWebPage(self)
self.page.loadFinished.connect(self.save)
self.startNext()
def currentUrl(self):
return self.urlList[self.counter][0]
def currentFilename(self):
return self.urlList[self.counter][1]
def startNext(self):
print "Downloading %s..."%self.currentUrl()
self.page.mainFrame().load(self.currentUrl())
def save(self, ok):
if ok:
data = self.page.mainFrame().toHtml()
with codecs.open(self.currentFilename(), encoding="utf-8", mode="w") as f:
f.write(data)
print "Saving %s to %s."%(self.currentUrl(), self.currentFilename())
else:
print "Error while downloading %s\nSkipping."%self.currentUrl()
self.counter += 1
if self.counter < len(self.urlList):
self.startNext()
else:
self.done.emit()
urlList = [("http://www.nsf.gov/awardsearch/simpleSearchResult?queryText=8","nsf.html"),
("http://www.nsf.gov/awardsearch/ExportResultServlet?exportType=txt","a.txt")]
app = QApplication(sys.argv)
downloader = Downloader(urlList)
# Quit when done
downloader.done.connect(app.quit)
# To view the pages
web = QWebView()
# To prevent user action that would interrupt the current page loading
web.setDisabled(True)
web.setPage(downloader.page)
web.show()
sys.exit(app.exec_())

Related

Empty image in QClipboard [duplicate]

I'm trying to run a simple PyQt5 application on Linux, the code is as follows:
#!/usr/bin/python
import sys
from PyQt5.QtWidgets import QApplication, QWidget
def main():
app = QApplication(sys.argv)
w = QWidget()
w.resize(250, 150)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
mime = app.clipboard().mimeData()
print(mime.hasImage()) # True
print(mime.imageData()) # None
sys.exit(app.exec_())
if __name__ == '__main__':
main()
Before running it, I copied an image into the clipboard, so mime.hasImage() should return True. No problem, that's also the case. But what's weird is, mime.imageData() sometimes returns None. that shouldn't happen. mime.imageData() should contain the image that I copied instead of None. Is there anything wrong with the code?
By the way, this seems to only happen on Linux, mime.imageData() never returns None on Windows. I'm using python3
That hasImage() returns True does not imply that imageData() returns a QImage since it only indicates that the user copied an image to the clipboard, and in what format do I copy the image? Well, it could be png, jpg, etc or it could provide the url for the client application to download or html to insert it into the client application and then obtain the image by rendering the HTML.
So in general the application from which the image was copied is responsible for the sending format and that there is no restrictive standard for that format but there are common formats.
The following example shows the logic to handle the images that come from urls and HTML:
#!/usr/bin/python
import sys
from functools import cached_property
from PyQt5.QtCore import pyqtSignal, QObject, QUrl
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
from PyQt5.QtGui import QGuiApplication, QImage, QPixmap
from PyQt5.QtWidgets import QApplication, QWidget, QLabel
from bs4 import BeautifulSoup
class ImageDownloader(QObject):
finished = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
self.manager.finished.connect(self.handle_finished)
#cached_property
def manager(self):
return QNetworkAccessManager()
def start_download(self, url):
self.manager.get(QNetworkRequest(url))
def handle_finished(self, reply):
if reply.error() != QNetworkReply.NoError:
print("error: ", reply.errorString())
return
image = QImage()
image.loadFromData(reply.readAll())
self.finished.emit(image)
class ClipboardManager(QObject):
imageChanged = pyqtSignal(QImage)
def __init__(self, parent=None):
super().__init__(parent)
QGuiApplication.clipboard().dataChanged.connect(
self.handle_clipboard_datachanged
)
self.downloader.finished.connect(self.imageChanged)
#cached_property
def downloader(self):
return ImageDownloader()
def handle_clipboard_datachanged(self):
mime = QGuiApplication.clipboard().mimeData()
if mime.hasImage():
image = mime.imageData()
if image is not None:
self.imageChanged.emit(image)
elif mime.hasUrls():
url = mime.urls()[0]
self.downloader.start_download(urls[0])
elif mime.hasHtml():
html = mime.html()
soup = BeautifulSoup(html, features="lxml")
imgs = soup.findAll("img")
if imgs:
url = QUrl.fromUserInput(imgs[0]["src"])
self.downloader.start_download(url)
else:
for fmt in mime.formats():
print(fmt, mime.data(fmt))
def main():
app = QApplication(sys.argv)
label = QLabel(scaledContents=True)
label.resize(250, 150)
label.move(300, 300)
label.setWindowTitle("Simple")
label.show()
manager = ClipboardManager()
manager.imageChanged.connect(
lambda image: label.setPixmap(QPixmap.fromImage(image))
)
sys.exit(app.exec_())
if __name__ == "__main__":
main()

Qt Designer Storing and Parsing User Input from Line Edit using Python to Robot Operating System (ROS)

I am currently trying to create a GUI that allows users to type in whatever words they want to be translated into R2-D2's voice.
I have used Qt 5 Designer to create a user input using Line Edit and a button that will publish the user input to a specified topic in ROS. I have also converted it into a python file using pyuic5 -o as r2d2_sound_control.py and I have a main file called main_r2d2_sound_control.py that I will run to initialise the GUI.
How do you store and parse a string from Line Edit using python? I do not know how to declare the string from the user input and parse it when the button is clicked. I'm using pyqt 4
Thank you in advance.
Link to R2-D2 voice package in GitHub https://github.com/koide3/ros2d2
Contents of r2d2_sound_control.py:
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'r2d2_sound_control.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(505, 114)
self.pushButton_speak = QtWidgets.QPushButton(Dialog)
self.pushButton_speak.setGeometry(QtCore.QRect(300, 40, 89, 25))
self.pushButton_speak.setObjectName("pushButton_speak")
self.lineEdit_speak = QtWidgets.QLineEdit(Dialog)
self.lineEdit_speak.setGeometry(QtCore.QRect(60, 40, 201, 25))
self.lineEdit_speak.setObjectName("lineEdit_speak")
self.retranslateUi(Dialog)
self.pushButton_speak.clicked.connect(Dialog.clicked_speak)
self.lineEdit_speak.returnPressed.connect(Dialog.clicked_speak)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.pushButton_speak.setText(_translate("Dialog", "Say Out"))
Contents of main_r2d2_sound_control.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# GUI
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
#Import the automatically generated file
from r2d2_sound_control import Ui_Dialog
# ROS
import rospy
from std_msgs.msg import String
class Test(QDialog):
def __init__(self,parent=None):
# GUI
super(Test, self).__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
# ROS pub settings
self.r2d2_sound_controller_String = String()
self.pub_r2d2_sound_controller_speak = rospy.Publisher('/ros2d2_node/speak',String,queue_size=10)
def speak_content(self):
self.input = string(lineEdit_speak)
self.lineEdit_speak.setText(self.text())
def clicked_speak(self):
"""
The slot name specified in Qt Designer.
Write the process you want to execute with the "Say Out" button.
"""
self.r2d2_sound_controller_String.data = text()
self.pub_r2d2_sound_controller_speak.publish(self.r2d2_sound_controller_String)
self.r2d2_sound_controller_String.data = ''
if __name__ == '__main__':
rospy.init_node('r2d2_sound_talker')
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())
I have managed to fix it thanks to my friend. I have made the following changes to main_r2d2_sound_control.py
#! /usr/bin/python3
# -*- coding: utf-8 -*-
# GUI
import sys
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
#Import the automatically generated file
from r2d2_head_gripper_sound import Ui_Dialog
# ROS
import rospy
from std_msgs.msg import String
class Test(QDialog):
def __init__(self,parent=None):
# GUI
super(Test, self).__init__(parent)
self.ui = Ui_Dialog()
self.ui.setupUi(self)
# ROS pub settings
self.r2d2_sound_controller_String = String()
self.pub_r2d2_sound_controller_speak = rospy.Publisher('/ros2d2_node/speak',String,queue_size=10)
self.r2d2_sound_controller_String.data = ''
def clicked_speak(self):
"""
The slot name specified in Qt Designer.
Write the process you want to execute with the "Say Out" button.
"""
text = self.ui.lineEdit_speak.text()
self.r2d2_sound_controller_String.data = text
self.pub_r2d2_sound_controller_speak.publish(self.r2d2_sound_controller_String)
if __name__ == '__main__':
rospy.init_node('r2d2_head_and_gripper_talker')
app = QApplication(sys.argv)
window = Test()
window.show()
sys.exit(app.exec_())

PyQt4 display dialog box on all displays windows 10

First off thanks for the help, I am a unix admin and don't know much about QT or windows 10
I have to display a qt popup menu/app on all displays on windows 10 using winexe to access a remote box, the behavior should be like the below winexe command:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'msg * hello jello'
below is my command line for the PyQt4 script:
winexe -U 'ad/user%xxxxxxx' --system --interactive=1 //10.14.6.133 'cmd /c c:\python26\python.exe c:\scripts\bobo3.py'
It just sits there and does nothing visible. My guess is it needs to be displayed to all active displays and I have no idea how to do this.
Here is the code, it works when run from a cmd prompt locally:
from optparse import OptionParser
from PyQt4 import QtGui, QtCore
class TopDialog(QtGui.QWidget):
def __init__(self, title='Message', width=960, height=640, ):
super(TopDialog, self).__init__()
self.setFixedSize(width, height)
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.WindowStaysOnTopHint)
screen = QtGui.QDesktopWidget().screenGeometry()
self.move((screen.width() - self.width())/2, (screen.height() - self.height())/2)
if not isinstance(title, unicode):
title = title.decode('utf-8')
self.title_lb = QtGui.QLabel(title)
self.title_lb.setAlignment(QtCore.Qt.AlignCenter)
self.title_lb.setFrameStyle(QtGui.QFrame.StyledPanel | QtGui.QFrame.Sunken)
self.message_lb = QtGui.QLabel()
self.message_lb.setAlignment(QtCore.Qt.AlignCenter)
self.message_lb.setMinimumSize(self.width()/2, self.height()/2)
self.ok_bt = QtGui.QPushButton('OK')
self.ok_bt.clicked.connect(self._ok_bt_clicked)
self.main_lo = QtGui.QVBoxLayout()
self.main_lo.addWidget(self.title_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.message_lb)
self.main_lo.addStretch()
self.main_lo.addWidget(self.ok_bt)
self.setLayout(self.main_lo)
def set_message(self, message):
if not isinstance(message, unicode):
message = message.decode('utf-8')
self.message_lb.setText(message)
def set_button_text(self, text):
if not isinstance(text, unicode):
text = text.decode('utf-8')
self.ok_bt.setText(text)
def _ok_bt_clicked(self):
# do something you want.
self.close() # close the window
client = None
def main():
global client
parser = OptionParser()
parser.add_option('-m', '--message', type=str, action="store", dest="message", help="The alert message")
parser.add_option('-s', '--sent_by', type=str, action="store", dest="sent_by", help='login of sender')
(options, args) = parser.parse_args()
app = QtGui.QApplication(sys.argv)
sent_by = options.sent_by
if sent_by is None:
sent_by = "Someone"
message = options.message
if message is None:
message = "we are not sure why"
print sent_by, message
client = TopDialog(' %s wants your attention' % (sent_by))
client.set_message('%s' % (message))
client.set_button_text('OK')
client.show()
app.exec_()
main()

how to load multiple pages one by one in QWebPage

I am trying to crawl news article pages for comments. After some research I found that mostly websites use an iframe for it. I want to get the "src" of the iframe. I am using QtWebKit in Python using PySide. It is actually working but just once. It is not loading other webpages. I am using the following code:
import sys
import pymysql
from PySide.QtGui import *
from PySide.QtCore import *
from PySide.QtWebKit import *
from pprint import pprint
from bs4 import BeautifulSoup
class Render(QWebPage):
def __init__(self, url):
try:
self.app = QApplication(sys.argv)
except RuntimeError:
self.app = QCoreApplication.instance()
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()
def visit(url):
r = Render(url)
p = r.frame.toHtml()
f_url = str(r.frame.url().toString())
return p
def is_comment_url(url):
lower_url = url.lower()
n = lower_url.find("comment")
if n>0:
return True
else:
return False
with open("urls.txt") as f:
content = f.read().splitlines()
list_of_urls = []
for url in content:
page = visit(url)
soup = BeautifulSoup(page)
for tag in soup.findAll('iframe', src=True):
link = tag['src']
if is_comment_url(link):
print(link)
list_of_urls += link
pprint(list_of_urls)
But the issue is it works only for single iteration and gets stuck.
Also is there any way to save a web page as it is as displayed by the browser (after executing all the javascript etc.)

catch link clicks in QtWebView and open in default browser

I am opening a page in QtWebView (in PyQt if that matters) and I want to open all links in the system default browser. I.e. a click on a link should not change the site in the QtWebView but it should open it with the default browser. I want to make it impossible to the user to change the site in the QtWebView.
How can I do that?
Thanks,
Albert
That does it:
import sys, webbrowser
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from PyQt4.QtWebKit import *
app = QApplication(sys.argv)
web = QWebView()
web.load(QUrl("http://www.az2000.de/projects/javascript-project/"))
web.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
def linkClicked(url): webbrowser.open(str(url.toString()))
web.connect(web, SIGNAL("linkClicked (const QUrl&)"), linkClicked)
web.show()
sys.exit(app.exec_())
Updated example for PyQt5 (the magic is to re-implement the "acceptNavigationRequest" method):
from PyQt5 import QtWidgets, QtCore, QtGui, QtWebEngineWidgets
class RestrictedQWebEnginePage(QtWebEngineWidgets.QWebEnginePage):
""" Filters links so that users cannot just navigate to any page on the web,
but just to those pages, that are listed in allowed_pages.
This is achieved by re-implementing acceptNavigationRequest.
The latter could also be adapted to accept, e.g. URLs within a domain."""
def __init__(self, parent=None):
super().__init__(parent)
self.allowed_pages = []
def acceptNavigationRequest(self, qurl, navtype, mainframe):
# print("Navigation Request intercepted:", qurl)
if qurl in self.allowed_pages: # open in QWebEngineView
return True
else: # delegate link to default browser
QtGui.QDesktopServices.openUrl(qurl)
return False
class RestrictedWebViewWidget(QtWidgets.QWidget):
"""A QWebEngineView is required to display a QWebEnginePage."""
def __init__(self, parent=None, url=None, html_file=None):
super().__init__(parent)
self.view = QtWebEngineWidgets.QWebEngineView()
self.page = RestrictedQWebEnginePage()
if html_file:
print("Loading File:", html_file)
self.url = QtCore.QUrl.fromLocalFile(html_file)
self.page.allowed_pages.append(self.url)
self.page.load(self.url)
elif url:
print("Loading URL:", url)
self.url = QtCore.QUrl(url)
self.page.allowed_pages.append(self.url)
self.page.load(self.url)
# associate page with view
self.view.setPage(self.page)
# set layout
self.vl = QtWidgets.QVBoxLayout()
self.vl.addWidget(self.view)
self.setLayout(self.vl)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
web = RestrictedWebViewWidget(url="YOUR URL") # or YOUR local HTML file
web.show()
sys.exit(app.exec_())
When you click a link that has the target="_blank" attribute, QT calls the CreateWindow method in QWebEnginePage to create a new tab/new window.
The key is to re-implement this method to, instead of opening a new tab, open a new browser window.
class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
def createWindow(self, _type):
page = WebEnginePage(self)
page.urlChanged.connect(self.open_browser)
return page
def open_browser(self, url):
page = self.sender()
QDesktopServices.openUrl(url)
page.deleteLater()
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.url = QUrl("https://stackoverflow.com/")
self.webView = QWebEngineView()
self.page = WebEnginePage(self.webView)
self.webView.setPage(self.page)
self.webView.load(self.url)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
web = MainWindow()
web.show()
sys.exit(app.exec_())

Resources