How to open hyperlink with target="_blank" in PyQtWebEngine? - python

I have made a web browser using pyqt5 and PyQtWebEngine.It works fine but when I click on a hyperlink with target="_blank" then it does not work but how will I fix it. You can view its source code by clicking on this link https://github.com/SaptakBhoumik/WebPlus
. Please review my code and tell me what to do

As noted in the docs:
_blank: usually a new tab, but users can configure browsers to open a new window instead.
That is, the objective is not to reload the page but to create a new tab, and then to obtain that request you must override the createWindow method of QWebEngineView (or QWebEnginePage):
from functools import cached_property
import sys
from PyQt5 import QtCore, QtGui, QtWidgets, QtWebEngineWidgets
class WebView(QtWebEngineWidgets.QWebEngineView):
def createWindow(self, type_):
if not isinstance(self.window(), Browser):
return
if type_ == QtWebEngineWidgets.QWebEnginePage.WebBrowserTab:
return self.window().tab_widget.create_tab()
class TabWidget(QtWidgets.QTabWidget):
def create_tab(self):
view = WebView()
index = self.addTab(view, "(Untitled)")
self.setTabIcon(index, view.icon())
view.titleChanged.connect(
lambda title, view=view: self.update_title(view, title)
)
view.iconChanged.connect(lambda icon, view=view: self.update_icon(view, icon))
self.setCurrentWidget(view)
return view
def update_title(self, view, title):
index = self.indexOf(view)
self.setTabText(index, title)
def update_icon(self, view, icon):
index = self.indexOf(view)
self.setTabIcon(index, icon)
class Browser(QtWidgets.QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setCentralWidget(self.tab_widget)
view = self.tab_widget.create_tab()
view.load(
QtCore.QUrl(
"https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_a_target"
)
)
#cached_property
def tab_widget(self):
return TabWidget()
def main():
app = QtWidgets.QApplication(sys.argv)
w = Browser()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
Note: I recommend that you review the official example: WebEngine Widgets Simple Browser Example, in addition to its implementation in PySide2 which is easily translatable to PyQt5 where this feature and many more are implemented.

Related

use QDesktopServices.openUrl() to open window created by QWebEnginePage::createWindow()

when a JavaScript program requests to open a document in a new window, QWebEnginePage::createWindow() would be called to create a new window, while I want to (1) open the new window using QDesktopServices.openUrl(url) instead, and (2) keep the view in my QWebEngineView unchanged. My solution cannot satisfy (2), so any simpler solutions ?
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtWebEngineWidgets import *
from PyQt5.QtWebEngineCore import *
import sys,os
class WebEnginePage(QWebEnginePage):
def __init__(self, parent, mdicts=[]):
super().__init__(parent)
self.backwardUrl=''
def acceptNavigationRequest(self, url, navigationType, isMainFrame): # Navigation requests can be delegated to the Qt application instead of having the HTML handler engine process them by overloading this function. This is necessary when an HTML document is used as part of the user interface, and not to display external data, for example, when displaying a list of results.# The QWebEngineUrlRequestInterceptor class offers further options for intercepting and manipulating requests.
# print('acceptNavigationRequest-----------------', navigationType, isMainFrame)
if self.backwardUrl and isMainFrame:
print('blocked------------',self.backwardUrl)
self.setUrl(self.backwardUrl)
QDesktopServices.openUrl(self.backwardUrl)
self.backwardUrl=''
return False
return True
def createWindow(self, windowType):
print('createWindow')
self.backwardUrl=self.url()
return self
class WebEngineView(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
# self.mousePressEvent=lambda event:print('mousePressEvent',event.pos())
# self.mouseMoveEvent=lambda event:print('mouseMoveEvent',event.pos())
self.webPage = WebEnginePage(self)#self.page() # QWebEnginePage()
self.setPage(self.webPage)
# self.setUrl(QUrl('https://dict.eudic.net/liju/en/good#TingLiju'))
self.webPage.setUrl(QUrl('https://dict.eudic.net/liju/en/good#TingLiju'))
if __name__ == "__main__":
app = QApplication(sys.argv)
webEngineView = WebEngineView()
webEngineView.show()
sys.exit(app.exec_())
A possible solution is to create a page that only serves to obtain the url, and when you get it, delete it and launch the url with QDesktopServices::openUrl():
class FakePage(QWebEnginePage):
def __init__(self, parent=None):
super().__init__(parent)
self.urlChanged.connect(self.handle_url_changed)
#pyqtSlot(QUrl)
def handle_url_changed(self, url):
QDesktopServices.openUrl(url)
self.deleteLater()
class WebEnginePage(QWebEnginePage):
def createWindow(self, windowType):
return FakePage(self)
class WebEngineView(QWebEngineView):
def __init__(self, parent=None):
super().__init__(parent)
self.webPage = WebEnginePage(self)
self.setPage(self.webPage)
self.webPage.setUrl(QUrl("https://dict.eudic.net/liju/en/good#TingLiju"))

Displaying Websites with Python

I am making an os in python, but I need a web browser. Currently I am using the os.startfile method to launch chrome, but I want another way. I want a program that a user can enter a webpage and displaying the web page without using chrome, firefox, safari etc.
Here is the basic framework I have:
from tkinter import *
import webbrowser as wb
window = Tk()
window.configure(bg="Dark Red")
window.geometry("1000x1000")
window.title("Hyper Web Browser")
window.iconbitmap("icon.ico")
''' Defined Functions'''
def submit_url():
wb.open_new_tab(Address_Bar.get())
file2write = open("History.txt", "a")
file2write.write(["\n", Address_Bar.get()])
return submit_url
'''Objects'''
Address_Bar = Entry(
bg="White",
bd=0,
font=("Comic", 25),
width=100
)
Tab_1 = Label(
bg="Red",
bd=0,
width=20,
height=3
)
Return = Button(
command=submit_url()
)
Address_Bar.place(x=20, y=60)
Tab_1.place(x=0, y=0)
Return.pack()
window.mainloop()
However, this program launches the web page into the user's default browser. Hence, I want to display the web page without using any other web browsers.
Here is a simpler version of webbrowser using PyQt5 :
import sys
from PyQt5 import QtWidgets,QtGui,QtCore
from PyQt5.QtWebEngineWidgets import *
app=QtWidgets.QApplication(sys.argv)
w=QWebEngineView()
w.load(QtCore.QUrl('https://google.com')) ## load google on startup
w.showMaximized()
app.exec_()
You can now add different widgets to it .
In python you have two most common ways to make a webbrowser 1. by using gtk webkit 2. by QtWebEngine under PyQt5.
Webkit is based upon Safari while QtWebEngine is based on Chromium. You can decide which one suits you the best. Hope it helps.
Something like this may be what you are looking for. This is a simple demo script that allows web browsing, without opening it in a default web browser such as chrome. (This is essentially a DIY web browser script) I hope this helps!
from functools import cached_property
import sys
import keyboard
from prompt_toolkit.key_binding import KeyBindings
from PyQt5 import QtCore, QtWidgets, QtWebEngineWidgets
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import pyqtSlot
bindings = KeyBindings()
class WebView(QtWebEngineWidgets.QWebEngineView):
def createWindow(self, type_):
if not isinstance(self.window(), Browser):
return
if type_ == QtWebEngineWidgets.QWebEnginePage.WebBrowserTab:
return self.window().tab_widget.create_tab()
class TabWidget(QtWidgets.QTabWidget):
def create_tab(self):
view = WebView()
index = self.addTab(view, "...")
self.setTabIcon(index, view.icon())
view.titleChanged.connect(
lambda title, view=view: self.update_title(view, title)
)
view.iconChanged.connect(lambda icon, view=view: self.update_icon(view, icon))
self.setCurrentWidget(view)
return view
def update_title(self, view, title):
index = self.indexOf(view)
if 'DuckDuckGo' in title:
self.setTabText(index, 'Search')
else:
self.setTabText(index, title)
def update_icon(self, view, icon):
index = self.indexOf(view)
self.setTabIcon(index, icon)
class Browser(QtWidgets.QMainWindow):
def __init__(self, parent=None):
#main browser functrion
super().__init__(parent)
self.setCentralWidget(self.tab_widget)
view = self.tab_widget.create_tab()
view.load(QtCore.QUrl("https://www.duckduckgo.com/"))
QtWebEngineWidgets.QWebEngineProfile.defaultProfile().downloadRequested.connect(self.on_downloadRequested)
#QtCore.pyqtSlot("QWebEngineDownloadItem*")
def on_downloadRequested(self, download):
old_path = download.url().path()
suffix = QtCore.QFileInfo(old_path).suffix()
path, _ = QtWidgets.QFileDialog.getSaveFileName(
self, "Save File", old_path, "*." + suffix
)
if path:
download.setPath(path)
download.accept()
#cached_property
def tab_widget(self):
return TabWidget()
def main():
app = QtWidgets.QApplication(sys.argv)
w = Browser()
w.show()
w.showMaximized()
sys.exit(app.exec_())
if __name__ == "__main__":
while True:
main()

Python/PyQt reopening QWebView shows blank page

I have this problem with QWebView not showing anything after I rerun QAplication. This small snippet displys the problem:
import sys
from PyQt4 import QtGui, QtWebKit, QtCore
app = QtGui.QApplication(sys.argv)
while True:
browser = QtWebKit.QWebView()
browser.setUrl(QtCore.QUrl('https://www.google.ca/#q=pyqt'))
browser.show()
app.exec_()
Upon running, the google search page for pyqt is shown, but once I close the widget, next one pops up as blank instead of the same search page. I was wondering what I'm doing wrong here?
I do not know why the page stays blank, but I'm certain you can easily achieve the same functionality without calling QApplication.exec_() multiple times.
An example achieving the same:
from PySide import QtGui, QtCore, QtWebKit
class MyBrowser(QtWebKit.QWebView):
closing = QtCore.Signal()
def __init__(self):
super().__init__()
def closeEvent(self, event):
self.closing.emit()
class MyApp(QtCore.QObject):
def __init__(self):
super().__init__()
def setup(self):
self.browser = MyBrowser()
self.browser.closing.connect(self.setup)
self.browser.setUrl(QtCore.QUrl('https://www.google.ca/#q=pyqt'))
self.browser.show()
app = QtGui.QApplication([])
a = MyApp()
a.setup()
app.exec_()

PyQt4 signals and slots - QToolButton

In PyQt4 I have a main window which when the settings button is clicked opens the settings dialog
from PyQt4 import QtCore, QtGui
import ui_Design, ui_Settings_Design
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
def __init__(self, parent=None):
super(MainDialog, self).__init__(parent)
self.setupUi(self)
self.settingsBtn.clicked.connect(lambda: self.showSettings())
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
The above works and my SettingsDialog is displayed but I cant get the setPageIndex to work
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(self.setPageIndex)
#QtCore.pyqtSlot()
def setPageIndex(self):
print 'selected'
self.settingsStackedWidget.setCurrentIndex(0)
The bookSettingsBtn is a QToolButton
self.bookSettingsBtn = QtGui.QToolButton(self.navigationFrame)
And the settingsStackedWidget is a QStackedWidget
self.settingsStackedWidget = QtGui.QStackedWidget(SettingsDialog)
At this point I am pretty confused on signals and slots and nothing I have read clears this up - if anyone can point out what I am doing wrong above and also potentially direct me to a good (beginners) guide / tutorial on signals and slots it would be greatly appreciated
I would also like to know how to make setPageIndex work as follows:
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)
I'm not sure why you're doing the following, but that's the issue:
def showSettings(self):
dialog = QtGui.QDialog()
dialog.ui = SettingsDialog()
dialog.ui.setupUi(dialog)
dialog.exec_()
SettingsDialog itself is a proper QDialog. You don't need to instantiate another QDialog.
Right now, you're creating an empty QDialog and then populate it with the same ui as SettingsDialog (i.e. setupUi(dialog)), then you show this dialog. But... The signal connection is for SettingsDialog, and the dialog you're showing doesn't have that.
Basically, you don't need that extra QDialog at all. The following should be enough:
def showSettings(self):
dialog = SettingsDialog()
dialog.exec_()
Ok. So here is an example how you pass an argument to a slot
from functools import partial
# here you have a button bookSettingsBtn:
self.bookSettingsBtn = QtGui.QPushButton("settings")
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, self.bookSettingsBtn.text()))
#pyqtSlot(str) # this means the function expects 1 string parameter (str, str) 2 string parameters etc.
def setPageIndex(self, selection):
print "you pressed button " + selection
In your case the argument would be an int. Of course you have to get the value from somewhere
and then put it in the partial part as the argument (here I just used the text of the button),
but you can use int, bool etc. Just watch the slot signature.
Here is a tutorial that helped me:
http://zetcode.com/gui/pyqt4/
I hope this helps.
Hey here I have a fully running example (just copy paste it in a python file and run it):
Maybe this helps you. It's a small example but here you see how it works.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from functools import partial
class MyForm(QMainWindow):
def __init__(self, parent=None):
super(MyForm, self).__init__(parent)
button1 = QPushButton('Button 1')
button2 = QPushButton('Button 2')
button1.clicked.connect(partial(self.on_button, button1.text()))
button2.clicked.connect(partial(self.on_button, button1.text()))
layout = QHBoxLayout()
layout.addWidget(button1)
layout.addWidget(button2)
main_frame = QWidget()
main_frame.setLayout(layout)
self.setCentralWidget(main_frame)
#pyqtSlot(str)
def on_button(self, n):
print "Text of button is: " + str(n)
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
form = MyForm()
form.show()
app.exec_()
So I dont really understand why but changing the way the settingsDialog is called from the MainWindow has fixed my problem. I guess the parent window needed to be specified??:
class MainDialog(QtGui.QMainWindow, ui_Design.Ui_arbCrunchUI):
....
def showSettings(self):
self.settingsDialog = QtGui.QDialog(self)
self.settingsDialog.ui = SettingsDialog(self)
self.settingsDialog.ui.show()
class SettingsDialog(QtGui.QDialog, ui_Settings_Design.Ui_SettingsDialog):
def __init__(self, parent=None):
super(SettingsDialog, self).__init__(parent)
self.setupUi(self)
self.bookSettingsBtn.clicked.connect(partial(self.setPageIndex, 1))
#QtCore.pyqtSlot(int)
def setPageIndex(self, selection):
self.settingsStackedWidget.setCurrentIndex(selection)

Subclassed QWebView doesn't react to Hyperlink Clicks

This is in Python/PySide.
I am trying to create my own Parental WebBrowser by overloading the PySide.QtWebKit.QWebView widget. Then whenever someone clicks a link on the widget I check to see if we are going to an invalid website, if not we proceed, if yes then I redirect to a generic page.
So I have subclassed the PySide.QtWebKit.QWebView, but I am not receiving notification of when a link is clicked. I have overridden the linkClicked function but the function never runs when a link is clicked?
What am I doing wrong? Why cant my function run/react to the hyperlink click "event"? Do I need to override the webpage object & not this class to react to link clicks?
import PySide.QtWebKit
import sys
from PyQt4 import QtGui
class BrowserWindow( PySide.QtWebKit.QWebView ):
# Class Variables:
def __init__( self, _parent ):
""" Constructor: """
super(BrowserWindow, self).__init__()
PySide.QtWebKit.QWebView(None)
def linkClicked(self, arg__1):
""" Post: """
#print("LINK CLICKED")
#text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog',
# 'Enter your name:')
self.load("http://yahoo.com")
def main():
app = QtGui.QApplication(sys.argv)
view = BrowserWindow(None) #PySide.QtWebKit.QWebView(None)
view.load("http://google.com")
view.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
There are several problems with the code you posted. Firstly, you are importing both PySide and PyQt4, which is not a good idea. Secondly, QWebView.linkClicked is a signal, not a protected method, so you can't override it. Thirdly, you are passing a string to QWebView.load, when you should be passing a QtCore.QUrl.
However, aside from those problems, you also need to set the linkDelegationPolicy on the web page in order to override its link handling.
Here's an edited version of your code which should fix all the problems:
from PySide import QtCore, QtGui, QtWebKit
class BrowserWindow(QtWebKit.QWebView):
def __init__(self, parent=None):
super(BrowserWindow, self).__init__()
self.linkClicked.connect(self.handleLinkClicked)
def handleLinkClicked(self, url):
print(url.toString())
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
view = BrowserWindow()
view.load(QtCore.QUrl("http://google.com"))
view.page().setLinkDelegationPolicy(
QtWebKit.QWebPage.DelegateAllLinks)
view.show()
sys.exit(app.exec_())

Categories