I need to reimplement the "copy link" of a QWebView in the context menu for doing some other things inside the routine.
The copy link is the only one that really works inside an ajax site so I'm trying to reimplement the "download from link" passing trough this method.
The problem is that I don't know how to reimplement the basic functions of the "copy link" for retrieving the URL.
You can add extra menu items by reimplementing QWebView.contextMenu and generating a standard menu with QWebPage.createStandardContextMenu.
Then all you need to do is get a hit-test result from the position the context menu was requested from to give you the url (if there is one).
Here's a simple demo of the basic ideas:
from PyQt4 import QtGui, QtWebKit
class Browser(QtWebKit.QWebView):
def __init__(self):
super(Browser, self).__init__()
self.setHtml('''
<html><head><title>Test Page</title>
<body>
<p>link</p>
</body>
</html>
''')
def contextMenuEvent(self, event):
menu = self.page().createStandardContextMenu()
hit = self.page().currentFrame().hitTestContent(event.pos())
url = hit.linkUrl()
if not url.isEmpty():
menu.addSeparator()
action = menu.addAction('Download')
action.triggered.connect(lambda: self.download(url))
menu.exec_(event.globalPos())
def download(self, url):
print('download:', url)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
browser = Browser()
browser.setGeometry(800, 200, 400, 200)
browser.show()
sys.exit(app.exec_())
Related
I’m using PyQt5 for a browser in python and I want to know how I can write code that makes changes to the internet (the web) in PyQt5. I want to be able to connect to the web and for example change where a website goes to. If someone searches google.com in their url bar I want the internet to go to my website.com.
My code is:
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtWebEngineWidgets import *
class MyWebBrowser(QMainWindow):
def __init__(self,):
super(MyWebBrowser, self).__init__()
self.window=QWidget()
self.window.setWindowTitle("Brave")
self.layout=QVBoxLayout()
self.horizontal = QHBoxLayout()
self.url_bar = QTextEdit()
self.url_bar.setMaximumHeight(30)
self.go_btn=QPushButton("Go")
self.go_btn.setMinimumHeight(30)
self.back_btn = QPushButton("<")
self.back_btn.setMinimumHeight(30)
self.forward_btn = QPushButton(">")
self.forward_btn.setMinimumHeight(30)
self.horizontal.addWidget(self.url_bar)
self.horizontal.addWidget(self.go_btn)
self.horizontal.addWidget(self.back_btn)
self.horizontal.addWidget(self.forward_btn)
self.browser=QWebEngineView()
self.go_btn.clicked.connect(lambda: self.navigate(self.url_bar.toPlainText()))
self.back_btn.clicked.connect(self.browser.back)
self.forward_btn.clicked.connect(self.browser.forward)
self.layout.addLayout(self.horizontal)
self.layout.addWidget(self.browser)
self.browser.setUrl(QUrl("http://www.google.com"))
self.window.setLayout(self.layout)
self.window.show()
def navigate(self,url):
if not url.startswith("http"):
url = "http://" + url
self.url_bar.setText(url)
self.browser.setUrl(QUrl(url))
app=QApplication([])
window=MyWebBrowser()
app.exec_()
In your code, the navigate function is what gets called when the Go button in the browser is clicked. You can make the navigate function modify the URL when the URL contains the string google.com:
def navigate(self, url):
if not url.startswith("http"):
url = "http://" + url
self.url_bar.setText(url)
# redirect to your website
if "google.com" in url:
url = "http://stackoverflow.com"
self.browser.setUrl(QUrl(url))
This way, when you try to visit google.com, the address bar will still say http://google.com, but the browser will actually be sent to http://stackoverflow.com.
I have a QTextBrowser() object:
self.PAddressLink = QTextBrowser()
I need to click on a link placed on this QTextBrowser, and it should open a new dialog box.
self.PAddressLink.setHtml("<html><body><a href=#>+Add Permanent Address</a></body></html>")
I can open the new window with the below code anyhow:
self.PAddressLink.anchorClicked.connect(self.AddPAddress) #self.AddPAddress is the method of displaying a dialog box.
But I need to know if I can place the self.AddPAddress in the href and avoid using the below extra statement:
self.PAddressLink.anchorClicked.connect(self.AddPAddress) #self.AddPAddress
Assuming all the methods are defined on the same object (e.g. self), you could set the method names in the href attribute:
self.PAddressLink.setHtml('...')
self.PAddressLink.anchorClicked.connect(self.handleLink)
and then use getattr to call the method:
def handleLink(self, url):
if url.scheme():
# handle normal urls here if necessary...
else:
getattr(self, url.toString())()
Here's a complete demo using both QLabel and QTextBrowser:
from PyQt5 import QtCore, QtGui, QtWidgets
html = """
<p>Url Link</p>
<p>Method Link</p>
"""
class Window(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.label = QtWidgets.QLabel(html)
self.browser = QtWidgets.QTextBrowser()
self.browser.setOpenLinks(False)
self.browser.setHtml(html)
layout = QtWidgets.QVBoxLayout(self)
layout.addWidget(self.browser)
layout.addWidget(self.label)
self.label.linkActivated.connect(self.handleLink)
self.browser.anchorClicked.connect(self.handleLink)
def handleLink(self, url):
url = QtCore.QUrl(url)
if url.scheme():
# handle real urls
QtGui.QDesktopServices.openUrl(url)
else:
# handle methods
getattr(self, url.toString())()
def myMethod(self):
QtWidgets.QMessageBox.information(self, 'Test', 'Hello World!')
if __name__ == '__main__':
app = QtWidgets.QApplication(['Test'])
window = Window()
window.setGeometry(600, 100, 300, 200)
window.show()
app.exec()
Most likely not. Atleast not any easy way. You'd be just reimplementing the signals and slots system most likely.
Just as with buttons, you have to connect the click signal to a slot. That's how it is designed to work.
I have a QWebView window that opens my local .html file.
This local .html file has an embed flash movie - .swf
It works nice, when I click on it, it gives me keyboard focus.
So, there's my problem, I want it to have focus right after launching my app, not how it behaves right now with this mouse click needed.
I tried modifying .html file with following:
<body onLoad="window.document.IP.focus();">
as mentioned in Adobe official solution
Besides that I tried:
<script>
window.onload = function() {
var input = document.getElementById("IP").focus();
}
</script>
where "IP":
<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://fpdownload.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=43,0,0,0" id="IP" width="1024" height="768" align="middle">
[...]
<embed src="IP.swf" quality="best" salign="lt" bgcolor="#ffffff" width="1024" height="768" swliveconnect="true" id="IP" name="IP" align="middle" allowscriptaccess="sameDomain" type="application/x-shockwave-flash" pluginspage="http://www.adobe.com/go/getflashplayer_pl">
</object>
Stackoverflow Q1
Stackoverflow Q2
Edit 1.
class PlayFlash(QWebView):
def __init__(self):
# QWebView
self.view = QWebView.__init__(self)
self.setWindowFlag(Qt.FramelessWindowHint)
self.resize(1024, 768)
self.move(0, 0)
# enable flashplayer plugin
self._settings = QWebSettings.globalSettings()
self._settings.setAttribute(QWebSettings.PluginsEnabled, True)
self.setFocusPolicy(Qt.StrongFocus)
self.mouse = Controller()
self.timer = QTimer()
self.timer.setInterval(2000)
self.timer.timeout.connect(self.focusOnFlash)
self.timer.start()
self.timer_count = 0
if __name__ == "__main__":
# plugin path
os.environ['QTWEBKIT_PLUGIN_PATH'] = os.path.abspath('/home/kamil/gitlab/PlayFlash')
app = QApplication(sys.argv)
view = PlayFlash()
view.load("file:///home/kamil/gitlab/PlayFlash/PlayFlash.html")
# view.load("https://pythonspot.com")
view.show()
view.setFocus()
view.focusOnFlash()
app.exec_()
I had to add a timer that executes simulated click to get focus on that swf embed in html. Without that it does not have focus.
I hope this question isn't too far-fetched. I'm good with Selenium and I've been working with PyQt4 recently. I want to use them both together with a program I'm currently working on and it'd work out a lot more smoothly if I could embed the controllable browser into a Qt4 frame or widget. Can this be done? And if so, how?
It doesn't have to be done with Selenium, I just want to be able to control the browser or at least show a webpage in a Qt widget or frame.
So after some research into methods other people have used, I figured it out.
The code I used came from a "very simple browser" module I obtained from here
I modified the code to be more customizable for my future self.
Here's my modified version of the code:
from PyQt4 import QtCore, QtGui, QtWebKit
class Browser(QtGui.QMainWindow):
def __init__(self, size=[800,600], frame=None, centralWidget=None, default_url='https://www.google.com', backButton=True, forwardButton=True, topBar=True):
"""
Initialize the browser GUI and connect the events
"""
self.showBackButton = backButton
self.showForwardButton = forwardButton
self.showTopBar = topBar
QtGui.QMainWindow.__init__(self)
self.resize(size[0],size[1])
if (centralWidget == None):
self.centralwidget = QtGui.QWidget(self)
else:
self.centralwidget = centralWidget
self.mainLayout = QtGui.QHBoxLayout(self.centralwidget)
self.mainLayout.setSpacing(0)
self.mainLayout.setMargin(1)
if (frame == None):
self.frame = QtGui.QFrame(self.centralwidget)
else:
self.frame = frame
self.gridLayout = QtGui.QVBoxLayout(self.frame)
self.gridLayout.setMargin(0)
self.gridLayout.setSpacing(0)
self.horizontalLayout = QtGui.QHBoxLayout()
if (self.showTopBar):
self.tb_url = QtGui.QLineEdit(self.frame)
if (self.showBackButton):
self.bt_back = QtGui.QPushButton(self.frame)
if (self.showForwardButton):
self.bt_ahead = QtGui.QPushButton(self.frame)
if (self.showBackButton):
self.bt_back.setIcon(QtGui.QIcon().fromTheme("go-previous"))
if (self.showForwardButton):
self.bt_ahead.setIcon(QtGui.QIcon().fromTheme("go-next"))
if (self.showBackButton):
self.horizontalLayout.addWidget(self.bt_back)
if (self.showForwardButton):
self.horizontalLayout.addWidget(self.bt_ahead)
if (self.showTopBar):
self.horizontalLayout.addWidget(self.tb_url)
self.gridLayout.addLayout(self.horizontalLayout)
self.html = QtWebKit.QWebView()
self.gridLayout.addWidget(self.html)
self.mainLayout.addWidget(self.frame)
#self.setCentralWidget(self.centralwidget) --- Not needed when embedding into a frame
if (self.showTopBar):
self.connect(self.tb_url, QtCore.SIGNAL("returnPressed()"), self.browse)
if (self.showBackButton):
self.connect(self.bt_back, QtCore.SIGNAL("clicked()"), self.html.back)
if (self.showForwardButton):
self.connect(self.bt_ahead, QtCore.SIGNAL("clicked()"), self.html.forward)
self.connect(self.html, QtCore.SIGNAL("urlChanged(const QUrl)"), self.url_changed)
self.default_url = default_url
if (self.showTopBar):
self.tb_url.setText(self.default_url)
self.open(self.default_url)
def browse(self):
"""
Make a web browse on a specific url and show the page on the
Webview widget.
"""
if (self.showTopBar):
url = self.tb_url.text() if self.tb_url.text() else self.default_url
self.html.load(QtCore.QUrl(url))
self.html.show()
else:
pass
def url_changed(self, url):
"""
Triggered when the url is changed
"""
if (self.showTopBar):
self.tb_url.setText(url.toString())
else:
pass
def open(self, url):
self.html.load(QtCore.QUrl(url))
self.html.show()
It could use some work at the moment, but I've tested it out and it's doing exactly what I need it to do. I tested it out with the following chunk of code that runs when the script is executed
if (__name__ == "__main__"):
app = QtGui.QApplication(sys.argv)
window = QtGui.QMainWindow()
window.resize(800,600)
myFrame = QtGui.QFrame(window)
myFrame.resize(200,200)
myFrame.move(10,10)
main = Browser(centralWidget=myFrame, default_url='https://www.google.com/', forwardButton=False, backButton=False, topBar=False)
window.show()
sys.exit(app.exec_())
Like I said, it could use work, but it does exactly what I needed it to do. Now I can embed it into a frame (with the size of my choosing) to use within another application.
Regarding my modifications: I made it possible to keep/remove the back button, forward button and top bar (for the URL). But the webbrowser is still controllable using the "open" function.
And if you wanted to open another webpage, it's as simple as the following
main.open('https://your.webpage.here.com')
I want to dive in Python by building a simple browser-application. I've mad a minimalistic webkitbrowser with a tutorial and now want to extend the program, but I'm stuck at some tiny problems I cannot solve.
Python 3.3.3
using Glade for the UI
The first step is to simply add a second scrolledWindow in which the developer-tools should load, immediately.
Here is my .ui-file so far, and this is the python-code:
from gi.repository import Gtk, WebKit
UI_FILE = "browser.ui"
class Browser:
"""A simple Webkit-Browser in GTK+"""
def __init__(self):
self.builder = Gtk.Builder()
self.builder.add_from_file(UI_FILE)
self.builder.connect_signals(self)
self.back = self.builder.get_object("back")
self.forward = self.builder.get_object("forward")
self.adress = self.builder.get_object("adress")
self.webview = WebKit.WebView()
scrolled_window = self.builder.get_object("scrolledwindow")
scrolled_window.add(self.webview)
self.settings = WebKit.WebSettings()
self.settings.set_property('enable-developer-extras', True)
self.webview.set_settings(self.settings)
self.devtools = WebKit.WebInspector()
scrolled_window_dev = self.builder.get_object("scrolledwindowDev")
scrolled_window_dev.add(self.devtools)
^^^^^
self.webview.connect("title-changed", self.on_title_changed)
self.window = self.builder.get_object("window")
self.window.show_all()
def on_title_changed(self, webview, frame, title):
self.window.set_title(title)
def on_button_clicked(self, button):
if button.get_stock_id() == Gtk.STOCK_GO_FORWARD:
self.webview.go_forward()
elif button.get_stock_id() == Gtk.STOCK_GO_BACK:
self.webview.go_back()
def on_entry_activate(self, widget):
url = widget.get_text()
if not "http://" in url:
url = "http://"+url
self.webview.load_uri(url)
def destroy(self, window):
Gtk.main_quit()
def main():
app = Browser()
Gtk.main()
if __name__ == "__main__":
main()
I get the error
TypeError: argument widget: Expected Gtk.Widget, but got
gi.repository.WebKit.WebInspector
Okay, this is stated in the reference of Webkit, that WebInspector is a GObject and not a GtkWidget. But I don't know what to do now.
So, can I make a GtkWidget from a GObject (if yes - how) or should I attach the dev-tools in a complete different way?
The inspector, as you noted, isn't a widget. It's a web page, so you need to create another webview for it. You do this by getting self.window.props.web_inspector (don't create a new inspector) and connecting to its inspect-web-view signal. Inside that signal handler, you need to create a new webview, add that webview to a window or wherever you want to display it, and return it.
You'll probably also want to handle the show-window, attach-window, detach-window, and close-window signals.
More documentation here: inspect-web-view
Example of running Inspector in separate window. Webkit-gtk.
This gist without many signals connected.
https://gist.github.com/alex-eri/53518825b2a8a50dd1695c69ee5058cc