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_())
Related
So i am making a GUI to identify seagulls.
I have made some QMainWindow's and would like the user to nagviate through these using buttons.
The first window works fine, and the user gets to the next page.
However, when clicking on buttons on page 2, nothing works, not even printing a simple statement. I am new to this and i am doing something wrong. I tried messing with init but am at a loss.
I am using a function
def openNewPage(self,b):
NewPage = b.text()
uic.loadUi(f"{NewPage}.ui",self)
self.show
to open a new page by having the buttons clicked on by the user have the name of the next file to be opened.
For example: the button for white head has the text "WhiteHead"
and will the open the file WhiteHead.ui
example:
self.bWhiteHead.clicked.connect(lambda:self.openNewPage(self.bWhiteHead))
Please help me understand why buttons work in one window and not in the next, thank you.
Here is the code:
import sys
from PyQt6.QtWidgets import QApplication, QMainWindow
from PyQt6.QtGui import QIcon
from PyQt6.QtCore import Qt
from PyQt6 import uic
import time
class MyApp(QMainWindow):
def __init__(self):
super().__init__()
uic.loadUi("SeagullPage1.ui",self)
self.setWindowTitle("SEAGULL IDENTIFIER")
self.bBlackHead.clicked.connect(lambda:self.openNewPage(self.bBlackHead))
self.bWhiteHead.clicked.connect(lambda:self.openNewPage(self.bWhiteHead))
def openNewPage(self,b):
NewPage = b.text()
uic.loadUi(f"{NewPage}.ui",self)
self.show
class WhiteHead(QMainWindow):
def __init__(self):
super().__init__
self.setWindowTitle("SEAGULL IDENTIFIER2")
self.BlackFeetButton.clicked.connect(lambda:self.openNewPage(self.BlackFeetButton))
self.PinkFeetButton.clicked.connect(lambda:self.openNewPage(self.PinkFeetButton))
self.YellowFeetButton.clicked.connect(lambda:self.openNewPage(self.YellowFeetButton))
def openNewPage(self,b):
print("hey")
NewPage = b.text()
uic.loadUi(f"{NewPage}.ui",self)
self.show
class YellowFeet(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("SEAGULL IDENTIFIER3")
self.RedBeakButton.clicked.connect(lambda:self.openNewPage(self.RedBeakButton))
self.NonReadBeakButton.clicked.connect(lambda:self.openNewPage(self.NonReadBeakButton))
def openNewPage(self,b):
print("hey")
NewPage = b.text()
uic.loadUi(f"{NewPage}.ui",self)
self.show
if __name__ == "__main__":
app = QApplication(sys.argv)
myApp = MyApp()
myApp.show()
app.exec()
#sys.exit(app.exec_())
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.
I am trying to return a path from a browse folder dialog box.
I have tried passing the instance object or even attribute to the call and setting it there:
self.browseBtn.clicked.connect(myExporter.browseFolder(self))
or
self.browseBtn.clicked.connect(myExporter.browseFolder(self.path))
But this doesn't work. It causes the browser dialog to pop open immediately upon load and then once you choose a folder it errors out with : Failed to connect signal clicked().
I have tried to set the clicked call to a return, with no luck:
result = self.browseBtn.clicked.connect(myExporter.browseFolder)
Can someone lead me in the right direction as far as how to return a value, when you are dealing with separate classes handling the UI and logic? Also... is it bad practice to be separating them like this? I know I could probably easily solve this if I threw everything into just one python file, but I figured that is not proper.
Here is my ui file (ui.py):
from PySide import QtCore, QtGui
class Ui_Dialog(object):
def __init__(self):
self.path =""
def setupUi(self, Dialog, myExporter):
Dialog.setObjectName("Dialog")
Dialog.resize(382, 589)
...
.......
.............
.................
self.retranslateUi(Dialog)
self.tabWidget.setCurrentIndex(1)
QtCore.QMetaObject.connectSlotsByName(Dialog)
self.browseBtn.clicked.connect(myExporter.browseFolder)
Here is my exporter file (exporter.py):
class Exporter(object):
def __init__(self):
...
......
def browseFolder(self):
...
.......
do something
...........
return path
Here is my load/test file (loadExporter.py):
import ui as interface
import exporter as exporter
from PySide import QtCore, QtGui
app = QtGui.QApplication.instance()
if app is None:
app = QtGui.QApplication(sys.argv)
Dialog = QtGui.QDialog()
myExporter = exporter.Exporter()
myUI = interface.Ui_Dialog()
myUI.setupUi(Dialog, myExporter)
Dialog.show()
app.exec_()
It's not necessarily bad to have them in separate files. Having a separate file for certain widgets is a good thing especially if those widgets can be reused.
I would have my main file have a QMainWindow class.
class MyWindow(QtGui.QMainWindow):
pass
if __name__ == "__main__":
QtGui.QApplication([])
mywindow = MyWindow()
mywindow.show()
sys.exit(QtGui.qApp.exec_())
Wrapping the app functionality in if __name__ == "__main__" prevents this code from being run when another file tries to import this file.
A signal (self.browserBtn.clicked) calls a callback method. Everything is an object in python.
def my_func():
pass
new_func_name = my_func # my_func can be reassigned like any variable
my_func is an object. self.browseBtn.clicked.connect(my_func) passes my_func as a variable to be called later.
When self.browserBtn.clicked.emit() happens (on user click) it is the same as calling the connected functions my_func(). Other signals may pass values to callback functions self.lineEdit.textChanged.connect(my_func) calls 'my_func(new_text)'
You want your function to call everything for you.
def open_file(filename=None):
"""Open a file."""
# If a filename isn't given ask the user for a file
if filename is None:
filename, ext = QtGUi.QFileDialog.getOpenFileName(None, "Open File", ".../My Documents/")
# You may have to use the ext to make a proper filename
# Open the file
with open(filename, "r") as file:
file.read()
self.browserBtn.clicked.connect(open_file)
Structure
...
import mywidget
class MyWindow(QtGui.QMainWindow):
def __init__(self):
super().__init__()
...
self.show_file = QtGui.QLineEdit()
self.setCentralWidget(self.show_file)
self.exporter = mywidget.Exporter()
self.browserBtn.clicked.connect(self.open_file)
def open_file(self, filename=None):
"""Open a file."""
path = self.exporter.browseFolder()
# Do something with the path
self.show_file.setText(path)
if __name__ == "__main__":
QtGui.QApplication([])
mywindow = MyWindow()
mywindow.show()
sys.exit(QtGui.qApp.exec_())
I don't think I fully understand what you're trying to achieve, but may I suggest the following solution.
exporter.py
# implementation
dialog.py (Main UI)
import PyQt4.QtGui as qg
from exporter import Exporter
class Dialog(qg.QDialog):
def __init__(self):
super().__init__()
self.path = None
self.setup_widgets()
def setup_widgets(self):
self.browse.clicked.connect(self.on_browse_clicked)
def on_browse_clicked(self):
self.path = Exporter().browse_folder()
main.py
import sys
import PyQt4.QtGui as qg
from dialog import Dialog
if __name__ == '__main__':
app = qg.QApplication(sys.argv)
dialog = Dialog()
dialog.show()
sys.exit(app.exec_())
This way you still have three files, but dialog.py imports exporter.py instead of main.py importing both of them.
As for returning values from signals, I don't know; I think signals are voids (do not return values).
Signals pass arguments to slots via their formal parameters. Usually slots have the same or fewer parameters than their signal counterparts'. It's slots that return values.
In file index.py this line works fine,
but in imported class similar line won't work! I can't understand why.
Then i click pushButton by mice, it not work, no BtnClck1 method is called and no print-SecondWindowPrint.
But if i call PushButton click programmatically, it works fine.
And PushButton works fine if i make connect from index.py
Here is full code on GitHub github.com/m0x3/test1
Here is code:
index.py
import sys
from PyQt5 import uic
from PyQt5.QtWidgets import QMainWindow, QApplication
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
# Set up the MainWindow from Designer.
uic.loadUi("mw.ui", self)
# Connect up the buttons.
self.pushButton.clicked.connect(self.BtnClck)
self.show()
def BtnClck(self):
# Set up the ContentWindow from Designer.
from form1 import form1
form1(self.mn_general)
self.mn_general.pushButton_2.clicked.connect(form1.BtnClck1) #this works fine
if __name__ == '__main__':
app = QApplication(sys.argv)
ex = MainWindow()
sys.exit(app.exec_())
form1.py
from PyQt5 import uic
class form1:
def __init__(self, obj):
super().__init__()
uic.loadUi("form1.ui", obj)
obj.pushButton.setText('TextChanged on init') #this works fine
obj.pushButton.clicked.connect(self.BtnClck1) #this NOT works
obj.pushButton.click() #this works fine!
def BtnClck1(self):
print('SecondWindowPrint')
MainWindow.mn_general.pushButton_2 calls form1.BtnClck1 as a static function. it's not clear but it works.
If it is good for you, you can define form1.BtnClck1 as static function:
class form1:
def __init__(self, obj):
...........
#staticmethod
def BtnClck1():
print('SecondWindowPrint')
Another way (better way) is put instance of form1 class in a public variable in MainWindow class. You can change BtnClck function in Index.py like this:
def BtnClck(self):
# Set up the ContentWindow from Designer.
from form1 import form1
self.Form=form1(self.mn_general,5)
self.mn_general.pushButton_2.clicked.connect(form1.BtnClck1) #this works fine
Why doesn't the following example work?
from PyQt4 import QtGui
import sys
class TestView(QtGui.QWidget):
def __init__(self):
super(TestView, self).__init__()
self.initUI()
def initUI(self):
self.btn = QtGui.QPushButton('Button', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
class TestViewController():
def __init__(self, view):
view.btn.clicked.connect(self.buttonClicked)
view.show()
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestView()
TestViewController(view)
app.exec_()
if __name__ == '__main__':
main()
The example is supposed to represent an MVC structure (like the one in Figure 4 -- without the Model) where the controller (TestViewController) receives a reference to the view (TestView) and connects the clicked signal from the view's button view.btn to its function self.buttonClicked.
I'm sure the line view.btn.clicked.connect(self.buttonClicked) is executed but, apparently, it has no effect. Does anyone knows how to solve that?
Update (awful solution):
In the example, if I replace the line
view.btn.clicked.connect(self.buttonClicked)
with
view.clicked = self.clicked
view.btn.clicked.connect(view.clicked)
it works. I'm still not happy with that.
The reason it is not working is because the controller class is being garbage collected before you can ever click anything for it.
When you set view.clicked = self.clicked, what you're actually doing is making one of the objects from the controller persist on the view object so it never gets cleaned up - which isn't really the solution.
If you store your controller to a variable, it will protect it from collection.
So if you change your code above to read:
ctrl = TestViewController(view)
You'll be all set.
That being said - what exactly you are trying to do here, I am not sure...it seems you're trying to setup an MVC system for Qt - but Qt already has a pretty good system for that using the Qt Designer to separate the interface components into UI (view/template) files from controller logic (QWidget subclasses). Again, I don't know what you are trying to do and this may be a dumb down version of it, but I'd recommend making it all one class like so:
from PyQt4 import QtGui
import sys
class TestView(QtGui.QWidget):
def __init__(self):
super(TestView, self).__init__()
self.initUI()
def initUI(self):
self.btn = QtGui.QPushButton('Button', self)
self.btn.resize(self.btn.sizeHint())
self.btn.move(50, 50)
self.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestView()
view.show()
app.exec_()
if __name__ == '__main__':
main()
Edit: Clarifying the MVC of Qt
So this above example doesn't actually load the ui dynamically and create a controller/view separation. Its a bit hard to show on here. Best to work through some Qt/Designer based examples/tutorials - I have one here http://bitesofcode.blogspot.com/2011/10/introduction-to-designer.html but many can be found online.
The short answer is, your loadUi method can be replace with a PyQt4.uic dynamic load (and there are a number of different ways to set that up) such that your code ultimately reads something like this:
from PyQt4 import QtGui
import PyQt4.uic
import sys
class TestController(QtGui.QWidget):
def __init__(self):
super(TestController, self).__init__()
# load view
uifile = '/path/to/some/widget.ui'
PyQt4.uic.loadUi(uifile, self)
# create connections (assuming there is a widget called 'btn' that is loaded)
self.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestController()
view.show()
app.exec_()
if __name__ == '__main__':
main()
Edit 2: Storing UI references
If it is easier to visualize this concept, you Can also store a reference to the generated UI object:
from PyQt4 import QtGui
import PyQt4.uic
import sys
class TestController(QtGui.QWidget):
def __init__(self):
super(TestController, self).__init__()
# load a view from an external template
uifile = '/path/to/some/widget.ui'
self.ui = PyQt4.uic.loadUi(uifile, self)
# create connections (assuming there is a widget called 'btn' that is loaded)
self.ui.btn.clicked.connect(self.buttonClicked)
def buttonClicked(self):
print 'clicked'
def main():
app = QtGui.QApplication(sys.argv)
view = TestController()
view.show()
app.exec_()
if __name__ == '__main__':
main()