QWebEngineView - Javascript Callback - python

What I am ultimately trying to accomplish is to capture the username and password that the user enters into a website. For example, if the user enters "test#example.com" as the email address into Facebook's login, then clicks submit, I want to store that email address in my PyQt Application.
The closest I've come to achieving this has been using a series of JavaScript commands to place a listener on the "Login Button" that returns the current value of the user parameter. My problem is that the callback that PyQt provides is for when the runJavaScript function is completed, not the javascript event listener. I'm wondering if there is any way to capture the callback function from the JavaScript function, or if there is a better way altogether for me to do this.
import os
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
from PyQt5.QtCore import QUrl, QEventLoop
from PyQt5.QtWebEngineWidgets import QWebEngineView
class WebPage(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)
self.load(QUrl("https://facebook.com"))
self.loadFinished.connect(self._on_load_finished)
#self.page().runJavaScript("document.getElementById("myBtn").addEventListener("click", displayDate)", print)
def _on_load_finished(self):
print("Finished Loading")
cmds = ["btn=document.getElementById('u_0_r')", # Login Button
"user=document.getElementsByName('email')[0]",
"function get_username(){return user.value}",
"btn.addEventListener('click', get_username)"]
self.page().runJavaScript("; ".join(cmds), lambda x: print("test: %s" % x))
if __name__ == "__main__":
app = QApplication(sys.argv)
web = WebPage()
web.show()
sys.exit(app.exec_()) # only need one app, one running event loop

I found a work around using the "urlChanged" signal that seems to work so far for my applications
import os
import sys
from PyQt5.QtWidgets import QApplication, QVBoxLayout, QWidget
from PyQt5.QtCore import QUrl, QEventLoop
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
class WebPage(QWebEngineView):
def __init__(self):
QWebEngineView.__init__(self)
self.current_url = ''
self.load(QUrl("https://facebook.com"))
self.loadFinished.connect(self._on_load_finished)
self.urlChanged.connect(self._on_url_change)
def _on_load_finished(self):
self.current_url = self.url().toString()
def _on_url_change(self):
self.page().runJavaScript("document.getElementsByName('email')[0].value", self.store_value)
def store_value(self, param):
self.value = param
print("Param: " +str(param))
if __name__ == "__main__":
app = QApplication(sys.argv)
web = WebPage()
web.show()
sys.exit(app.exec_()) # only need one app, one running event loop

Related

Add widget (QCheckBox) to QFileDialog in PyQt5 (Python) not working

I would like to add a QCheckBox to a QFileDialog. I would like to use the static method QFileDialog.getSaveFileName() to show the dialog.
I have found several similar questions, all in c++:
How to add checkbox to QFileDialog window in QT3?
Adding a widget in QFileDialog
https://www.qtcentre.org/threads/42858-Creating-a-Custom-FileOpen-Dialog
https://forum.qt.io/topic/103964/add-checkbox-to-qfiledialog/7
I did my best to translate these discussions to python, but have not gotten to the solution yet. My code runs, but the checkbox does not show up, even when I use QFileDialog.DontUseNativeDialog.
This is how I am subclassing QFileDialog:
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtWidgets import QCheckBox
class ChkBxFileDialog(QFileDialog):
def __init__(self, chkBxTitle=""):
super().__init__()
self.setOption(QFileDialog.DontUseNativeDialog)
chkBx = QCheckBox(chkBxTitle)
self.layout().addWidget(chkBx)
#end __init__
#end ChkBxFileDialog
I have run this in two ways.
Option 1 (with extra QFileDialog.DontUseNativeDialog):
import sys
from PyQt5.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication(sys.argv)
fileDialog = ChkBxFileDialog(chkBxTitle="Chkbx")
fileName = fileDialog.getSaveFileName(filter='*.txt', initialFilter='*.txt',
options=QFileDialog.DontUseNativeDialog)[0]
sys.exit(app.exec_())
Option 2 (without extra QFileDialog.DontUseNativeDialog):
import sys
from PyQt5.QtWidgets import QApplication
if __name__ == "__main__":
app = QApplication(sys.argv)
fileDialog = ChkBxFileDialog(chkBxTitle="Chkbx")
fileName = fileDialog.getSaveFileName(filter='*.txt', initialFilter='*.txt')[0]
sys.exit(app.exec_())
The checkbox doesn't show with either option. Option 1 uses different window styling. Option 2 shows the typical PyQt QFileDialog.
Does anyone know what I am missing?
The problem is that getSaveFileName is a static method so they do not inherit from ChkBxFileDialog and therefore do not have the custom behavior.
There are 2 options:
Don't use getSaveFileName but implement the logic using QFileDialog directly:
import sys
from PyQt5.QtWidgets import QApplication, QCheckBox, QDialog, QFileDialog
class ChkBxFileDialog(QFileDialog):
def __init__(self, chkBxTitle="", filter="*.txt"):
super().__init__(filter=filter)
self.setSupportedSchemes(["file"])
self.setOption(QFileDialog.DontUseNativeDialog)
self.setAcceptMode(QFileDialog.AcceptSave)
self.selectNameFilter("*.txt")
chkBx = QCheckBox(chkBxTitle)
self.layout().addWidget(chkBx)
def main():
app = QApplication(sys.argv)
dialog = ChkBxFileDialog()
if dialog.exec_() == QDialog.Accepted:
filename = dialog.selectedUrls()[0].toLocalFile()
print(filename)
if __name__ == "__main__":
main()
Use some trick to get the QFileDialog instance, such as getting all the topLevels and verifying that it is a QFileDialog.
import sys
from functools import partial
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication, QCheckBox, QDialog, QFileDialog
def add_checkbox(chkBxTitle):
for tl in QApplication.topLevelWidgets():
if isinstance(tl, QFileDialog):
tl.setOption(QFileDialog.DontUseNativeDialog)
chkBx = QCheckBox(chkBxTitle)
tl.layout().addWidget(chkBx)
def main():
app = QApplication(sys.argv)
QTimer.singleShot(1000, partial(add_checkbox, ""))
fileName, _ = QFileDialog.getSaveFileName(
filter="*.txt", initialFilter="*.txt", options=QFileDialog.DontUseNativeDialog
)
if __name__ == "__main__":
main()
I would like to use the static method QFileDialog.getSaveFileName() to show the dialog
That's not possible. The static method as is defined in the C++ code knows nothing about your derived class, so it will create an instance of the base class, which doesn't contain your modifications. You have to explicitly instantiate your derived class, call exec() on the instance, check the return code and possibly call its selectedFiles() method to see what files were selected.

PyQt5 QWebEngineView not Displaying Anything

So I'm building a simple Web Browser using PyQt5, with QtWebEngineView, it works fine, but when I type something in the address bar and hit enter, the current page changes but the entered web address does not load, the screen remains blank.
It doesn't work with https:// either
"""A Simple Web Browser Written in Python and PyQt5"""
import sys
from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QIcon
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWidgets import QApplication, QMainWindow, QToolBar, QAction, QLineEdit
class PyChromeWindow(QMainWindow):
DEFAULT_SEARCH_ENGINE = QUrl("https://www.google.com")
def __init__(self):
super(PyChromeWindow, self).__init__()
self.browser = QWebEngineView(self)
self.browser.setUrl(self.DEFAULT_SEARCH_ENGINE)
# ToolBar
self.browser_tool_bar = QToolBar()
self.addToolBar(self.browser_tool_bar)
# Back Action
self.back_btn = QAction(QIcon('./resources/back_arrow16px.png'), 'Back')
self.back_btn.triggered.connect(self.browser.back)
self.browser_tool_bar.addAction(self.back_btn)
# Forward Action
self.forward_action = QAction(QIcon('./resources/forward_arrow16px.png'), 'Forward')
self.forward_action.triggered.connect(self.browser.forward)
self.browser_tool_bar.addAction(self.forward_action)
# Refresh Action
self.refresh_action = QAction(QIcon('./resources/refresh_icon16px.png'), 'Refresh')
self.refresh_action.triggered.connect(self.browser.reload)
self.browser_tool_bar.addAction(self.refresh_action)
# Home Action
self.home_action = QAction(QIcon('./resources/home_icon16px.png'), 'Home')
self.home_action.triggered.connect(lambda: self.browser.setUrl(self.DEFAULT_SEARCH_ENGINE))
self.browser_tool_bar.addAction(self.home_action)
# Address Bar
self.address_bar = QLineEdit()
self.address_bar.returnPressed.connect(self.navigate_to_url)
self.browser_tool_bar.addWidget(self.address_bar)
self.setCentralWidget(self.browser)
self.showMaximized()
def navigate_to_url(self):
"""Navigate to a specific URL"""
url = QUrl(self.address_bar.text())
self.browser.load(url)
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setApplicationName("PyChrome")
window = PyChromeWindow()
app.exec_()
"www.google.com" is not a valid url for QWebEngineView, in this case you must use QUrl::fromUserInput() which deduces a valid url.
url = QUrl.fromUserInput(self.address_bar.text())

PyQt5 are taking the screenshot without waiting the api response

I using pyqt5 for taking the screenshot to the URL. it's the react component. it takes the screenshot only component. don't wait till the API response. wanna take a screenshot after get the API response.
import sys
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt, QUrl, QTimer
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class Screenshot(QWebEngineView):
def capture(self, url, output_file):
self.output_file = output_file
self.load(QUrl(url))
self.loadFinished.connect(self.on_loaded)
self.setAttribute(Qt.WA_DontShowOnScreen)
self.show()
def on_loaded(self):
size = self.page().contentsSize().toSize()
self.resize(size)
qt = QTimer()
qt.setInterval(8000)
qt.setSingleShot(True)
qt.singleShot(8000, self.take_screenshot)
def take_screenshot(self):
self.grab().save(self.output_file, b'PNG')
self.app.quit()
app = QApplication([])
s = Screenshot()
s.app = app
s.capture('https://doc.qt.io/qt-5/qtimer.html', 'webpage20.png')
app.exec_()
Note: Under s.capture param URL is just for the example purpose.

itemClicked in QlistWidget, function executed more than 1 time

I have a strange problem, hope someone can clear it for me
import os
from os import path
import sys
import pathlib
from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QMainWindow, QLabel, QGridLayout, QWidget,
QWizard, QWizardPage, QLineEdit, \
QTabWidget, QApplication,
QTextEdit,QToolTip,QPushButton,QMessageBox
from PyQt5.QtCore import QSize,pyqtSlot,pyqtProperty
from PyQt5.QtGui import QFont
from PyQt5.uic import loadUiType
app = QApplication(sys.argv)
if getattr(sys, 'frozen', False):
# we are running in a bundle
installPath = sys._MEIPASS
print('we are running in a bundle')
else:
# we are running in a normal Python environment
installPath = os.path.dirname(os.path.abspath(__file__))
print('we are running in a normal Python environment')
UI_File, _ = loadUiType(path.join(path.dirname(__file__), 'test.ui'))
class MainAPP(QTabWidget, UI_File):
def __init__(self, parent=None):
super(MainAPP, self).__init__(parent)
self.setupUi(self)
self.handle_buttons()
def handle_buttons(self):
self.pushButton.clicked.connect(self.test_2)
def test_2(self):
for i in range(10):
self.listWidget.addItem(str('lklk'))
self.listWidget.itemClicked.connect(self.test)
def test(self):
for i in range(10):
self.listWidget_2.addItem(str('DDD'))
self.listWidget_2.itemClicked.connect(self.test_3)
def test_3(self):
print ('hi')
def main():
app = QApplication(sys.argv)
main = MainAPP()
main.show()
app.exec_()
if __name__ == "__main__":
main()
so basically, I have a push button, if I click on it it will display some data at listWidget and if I clicked on any item in listWidget , it will display other data on ListWidget_2 and then if I click on item in List_widget_2 it then should print ('Hi')
the problem is if I click multiple times in ListWidget and then click on an item in ListWidget_2 , I received more than one ('Hi) , it will diplay ('Hi') according to the number of clicks I clicked in the Listwidget
any idea what could be the issue
You only need to make a connection between a signal and a slot once. Currently you are making additional connections each time you click an item in the first list widget, which results in your method printing "hi" executing once for every connection you made.
To fix this, make both of the signal connections either in the test_2 method or in the __init__ method

Opening another window through pushbutton in GUI in PyQt5

I am new to Python and to PyQt. I have designed GUIs in MATLAB so its been a frustrating new experience. Right now, I have a main window and I want to open another login window on button push. What I have not decided is whether it should return the value of login to the parent window or another window. But either way I need to be able to open the login window through the pushbutton. I have created 2 files declaring the GUI one as a mainwindow and another as Dialog (does not seem like it is inheriting from QDialog though). The other 2 py files are classed calling the UIs separately and work fine. Each have their functions. I am pasting the main.py (mainwindow init and onOpen function code) along with logindialog.py init function code. Kindly help or else I will have to go back to Matlab where I can do this quite easily.`
import sys
from PyQt5 import QtWidgets, QtGui
from LoginDialog import LoginDialog
import UI_MSLDB_Main
from UI_LoginDialog import Ui_LoginDialog
#import Helper
#import Auth
#import TestFeature01
class Main(QtWidgets.QMainWindow, UI_MSLDB_Main.Ui_MSLDatabase):
def _init_(self, parent = None):
QtWidgets.QMainWindow._init_(self)
self.setupUi(self)
self.child = LoginDialog(self)
self.child.setupUi(QtWidgets.QDialog())
# self.createConnections()
self.pushButton_OpenPrimarylist.clicked.connect(self.onOpen)
def onOpen(self):
# window = QtWidgets.QDialog
# self.child = LoginDialog(window, Ui_LoginDialog)
# self.child.show()
# exec('LoginDialog.py')
self.child.show
# self.actionStudy.triggered.connect()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
# LoginDialog(QDialog, Ui_LoginDialog)
# loginDialog = LoginDialog(QDialog, UI_LoginDialog)
# username = loginDialog.username
# password =loginDialog.password
# Helper.dbConnect(username,password)
# isAuth = False
# result = -1
# while not isAuth:
# result = loginDialog.exec()
# if result == LoginDialog.Success or result == LoginDialog.Rejected:
# isAuth = True
# else:
# isAuth = False
result = 1
if result == 1:#LoginDialog.Success:
MSLDatabase = QtWidgets.QMainWindow()
ui = UI_MSLDB_Main.Ui_MSLDatabase()
ui.setupUi(MSLDatabase)
MSLDatabase.show()
# w.show()
app.exec_()
sys.exit(app.exec_())
There were multiple things I tried not knowing which would work. The commented codes are the ones I have tried and failed. LoginDialog class is below.
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QDialog
from PyQt5.QtWidgets import QMessageBox
import Auth
import Helper
from UI_LoginDialog import Ui_LoginDialog
class LoginDialog(QDialog, Ui_LoginDialog):
Success, Failed, Rejected, username, password = range(0,5)
def _init_(self):
QDialog._init_(self)
self.setupUi(self)
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL
(_fromUtf8("accepted()")),self.onAccept)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(_fromUtf8("rejected()")),
self.onReject)

Categories