I'm planning on executing a javascript function from pyqt QWebEngine. I followed a example which was using a map and map bound were retrieved when a Qt application button was pushed, and wrote a small example.
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<script>
function helloWorld(param1, param2) {
return "Hello world " + param1 + " " + param2;
}
</script>
</body>
</html>
Python:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtWebEngineWidgets import QWebEngineView
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.form_widget = FormWidget(self)
_widget = QWidget()
_layout = QVBoxLayout(_widget)
_layout.addWidget(self.form_widget)
self.setCentralWidget(_widget)
class FormWidget(QWidget):
def __init__(self, parent):
super(FormWidget, self).__init__(parent)
self.__controls()
self.__layout()
self.browser.page().runJavaScript("helloWorld()", self.ready)
def __controls(self):
html = open('test.html', 'r').read()
self.browser = QWebEngineView()
self.browser.setHtml(html)
def __layout(self):
self.vbox = QVBoxLayout()
self.hBox = QVBoxLayout()
self.hBox.addWidget(self.browser)
self.vbox.addLayout(self.hBox)
self.setLayout(self.vbox)
def ready(self, returnValue):
print(returnValue)
def main():
app = QApplication(sys.argv)
win = MainWindow()
win.show()
app.exec_()
if __name__ == '__main__':
sys.exit(main())
Nevertheless, I get the following error:
js: Uncaught ReferenceError: helloWorld is not defined
None ----> This is the return value printed at ready()
What am I missing?
You have 2 errors:
You are calling the function when the page has not yet finished loading.
Your function needs 2 parameters, although it also works but will signal undefined.
Using the above you get the following:
import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget
from PyQt5.QtWebEngineWidgets import QWebEngineView
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
self.form_widget = FormWidget(self)
_widget = QWidget()
_layout = QVBoxLayout(_widget)
_layout.addWidget(self.form_widget)
self.setCentralWidget(_widget)
class FormWidget(QWidget):
def __init__(self, parent):
super(FormWidget, self).__init__(parent)
self.__controls()
self.__layout()
def __controls(self):
html = open('test.html', 'r').read()
self.browser = QWebEngineView()
self.browser.setHtml(html)
self.browser.loadFinished.connect(self.onLoadFinished)
def onLoadFinished(self, ok):
if ok:
self.browser.page().runJavaScript("helloWorld(1, \"2\")", self.ready)
def __layout(self):
self.vbox = QVBoxLayout()
self.hBox = QVBoxLayout()
self.hBox.addWidget(self.browser)
self.vbox.addLayout(self.hBox)
self.setLayout(self.vbox)
def ready(self, returnValue):
print(returnValue)
def main():
app = QApplication(sys.argv)
win = MainWindow()
win.show()
return app.exec_()
if __name__ == '__main__':
sys.exit(main())
plus:
To visualize the output of the js console you must overwrite the javaScriptConsoleMessage method of QWebEnginePage:
...
class WebEnginePage(QWebEnginePage):
def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID):
print("javaScriptConsoleMessage: ", level, message, lineNumber, sourceID)
class FormWidget(QWidget):
...
def __controls(self):
...
self.browser = QWebEngineView()
self.browser.setPage(WebEnginePage(self.browser))
...
To verify this you must use console.log(...):
*.html
...
<script>
function helloWorld(param1, param2) {
console.log("Hello world")
return "Hello world " + param1 + " " + param2;
}
</script>
...
Output:
javaScriptConsoleMessage: 0 Hello world 12 data:text/html;charset=UTF-8,%3C%21DOCTYPE%20html%3E%0A%3Chtml%3E%0A%0A%3Chead%3E%0A%20%20%20%20%3Cmeta%20charset%3D%22UTF-8%22%3E%0A%3C%2Fhead%3E%0A%0A%3Cbody%3E%0A%0A%3Cscript%3E%0A%20%20%20%20function%20helloWorld%28param1%2C%20param2%29%20%7B%0A%20%20%20%20%09console.log%28%22Hello%20world%22%29%0A%20%20%20%20%20%20%20%20return%20%22Hello%20world%20%22%20%2B%20param1%20%2B%20%22%20%22%20%2B%20param2%3B%0A%20%20%20%20%7D%0A%3C%2Fscript%3E%0A%0A%3C%2Fbody%3E%0A%3C%2Fhtml%3E
Related
The example of the code snippet is here:
from PySide2 import QtCore, QtGui, QtWidgets, QtWebChannel
from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class AppWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.view = WebView(self)
self.setCentralWidget(self.view)
self.view.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
self.view.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True);
self.page = self.view.page()
self.page.setDevToolsPage(self.page)
#self.inspector = QWebInspector(self) # Crash is here!!!
#self.inspector.setPage(self.page)
#self.inspector.hide()
The three lines above were working perfectly in previous versions.
What is the alternative to QWebInspector in PySide2?
There is no equivalent QWebInspector class for QWebEngine, because the dev tools are provided by the underlying Chrome browser. An environment variable needs to be set to enable the tools, and you can then access them via a separate Chrome-based browser - see Qt WebEngine Developer Tools in the Qt5 docs for the full details.
Below is a simple demo based on your example code:
import sys, os
DEBUG_PORT = '5588'
DEBUG_URL = 'http://127.0.0.1:%s' % DEBUG_PORT
os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = DEBUG_PORT
from PySide2 import QtCore, QtGui, QtWidgets, QtWebChannel
from PySide2.QtWebEngineWidgets import QWebEngineView, QWebEngineSettings
class AppWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.view = QWebEngineView(self)
self.setCentralWidget(self.view)
self.view.settings().setAttribute(QWebEngineSettings.JavascriptEnabled, True)
self.view.settings().setAttribute(QWebEngineSettings.PluginsEnabled, True)
self.view.loadFinished.connect(self.handleLoaded)
self.view.load(QtCore.QUrl('https://google.com/'))
self.inspector = QWebEngineView()
self.inspector.setWindowTitle('Web Inspector')
self.inspector.load(QtCore.QUrl(DEBUG_URL))
def handleLoaded(self, ok):
if ok:
self.view.page().setDevToolsPage(self.inspector.page())
self.inspector.show()
app = QtWidgets.QApplication(sys.argv)
win = AppWindow()
win.setGeometry(600, 100, 600, 480)
win.show()
app.exec_()
I am trying to make a quart application using multiple websockets at the same time:
<script type="text/javascript">
let socket = new WebSocket('ws://localhost:5000/mensagens/{{ dialog_id }}');
socket.onmessage = function(event) {
var messages_dom = document.getElementsByTagName('ul')[0];
var message_dom = document.createElement('li');
var cotent_dom = document.createTextNode(event.data);
message_dom.appendChild(cotent_dom);
messages_dom.appendChild(message_dom);
};
</script>
<script>
let ws = new WebSocket('ws://localhost:5000/printar/12');
function myFunction() {
var x = document.getElementById("myText").value;
ws.send(x);
document.getElementById("demo").innerHTML = x;
};
</script>
And this is the server side:
#Quart
from quart import Quart, render_template, url_for, websocket, redirect, request
#asyncio
import asyncio
app = Quart(__name__)
...
#websocket da conversa individual
#app.websocket('/mensagens/<dialog_id>')
async def mensagens(dialog_id):
print("123");
try:
output = await ".....function that updates from the SQLite database....";
await websocket.send(f"{output}");
await asyncio.sleep(1);
except Exception as e:
print("-------");
print(e);
#websocket de enviar mensagens
#app.websocket('/printar/<dialog_id>')
async def printar(dialog_id):
print("aqui");
try:
while True:
print(dialog_id);
data = await websocket.receive();
print(data + "\n=====");
except Exception as e:
print(e);
if __name__ == "__main__":
try:
app.run();
except KeyboardInterrupt:
print("=====\nAdeus!\n=====");
except Exception as e:
print(e);
However, for some reason, the second websocket only begin to run after the first websocket receives an update from "mensagens". I really don't understand what and why is happening.
Is there an example of a chat server made with Quart that I could look at?
So... I am idiot. I just found this where explains the user of receive and send indepedently (also, I am not that good with asyncio).
Anyway, here is an example:
<!doctype html>
<html>
<head>
<title>Quart + SQLite</title>
</head>
<body>
<h1>Testes!</h1>
<div class="mensagens">
<ul></ul>
</div>
Mensagem: <input type="text" id="myText" value="Mickey">
<p>Escreva suas mensagens acima!</p>
<button onclick="myFunction()">Enviar</button>
<p id="demo"></p>
<script>
let socket = new WebSocket('ws://localhost:5000/ws/12');
function myFunction() {
var x = document.getElementById("myText").value;
socket.send(x);
document.getElementById("demo").innerHTML = x;
};
socket.onmessage = function(event) {
var messages_dom = document.getElementsByTagName('ul')[0];
var message_dom = document.createElement('li');
var cotent_dom = document.createTextNode(event.data);
message_dom.appendChild(cotent_dom);
messages_dom.appendChild(message_dom);
};
</script>
</body>
</html>
And here is the python side:
#https://pgjones.gitlab.io/quart/how_to_guides/websockets.html
#Quart
from quart import Quart, render_template, url_for, websocket, redirect, request
import asyncio
app = Quart(__name__)
async def sending():
while True:
await websocket.send('f{1}');
asyncio.sleep(1);
async def receiving():
while True:
data = await websocket.receive();
print(data + "\n=====");
#app.websocket('/ws/<num>')
async def ws(num):
print(num);
producer = asyncio.create_task(sending());
consumer = asyncio.create_task(receiving());
await asyncio.gather(producer, consumer);
#app.route('/')
async def main():
return await render_template('teste_html.html');
if __name__ == '__main__':
try:
app.run();
except KeyboardInterrupt:
print("=====\nAdeus!\n=====");
except Exception as e:
print(e);
I want to download a file and at the same time redirect to a new page. The user is shown a page with a link for the download. When they click it, I want to start the download then redirect to another page so they can't keep clicking it. However, I can only seem to get one or the other to work.
from flask import Flask, render_template, redirect, url_for, session, request, flash, send_file
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, BooleanField
from pytube import YouTube
import os
import random, string
import re
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mysecretkey'
session_id = ''.join(random.choices(string.ascii_letters + string.digits, k=6))
class url_form(FlaskForm):
link = StringField('Enter link of YouTube video you would like to convert...')
audio = BooleanField('Audio Only')
submit = SubmitField('Convert')
def download_video(link, just_audio):
yt = YouTube(link)
download_path = 'conversions/'
#global myvar
if just_audio == True:
stream = yt.streams.filter(only_audio=True).first()
tag = 'video'
else:
stream = yt.streams.filter(adaptive=True).first()
tag = 'audio only'
download_video.name = yt.title
download_video.cleanString = re.sub('[^a-zA-Z0-9 \n\.]', '', download_video.name)
download_video.thumbnail = yt.thumbnail_url
download_video.path = stream.download(filename = download_video.cleanString, output_path = download_path)
return
#app.route('/', methods=['GET', 'POST'])
def index():
result = False
form = url_form()
if form.validate_on_submit():
session['link'] = form.link.data
if form.audio.data:
just_audio = True
else:
just_audio = False
session['just_audio'] = just_audio
link = session.get('link')
just_audio = session.get('just_audio')
download_video(link, just_audio)
#download_video(link, just_audio)
#return send_file(download_video.path, attachment_filename=download_video.cleanString + '.mp4', as_attachment=True)
return redirect(url_for('conversion_complete'))
return render_template('index.html', form=form, result=result)
#app.route('/conversion_complete', methods=['GET', 'POST'])
def conversion_complete():
return render_template('conversion_complete.html')
#app.route('/download/', methods=['GET'])
def download():
return send_file(download_video.path, attachment_filename=download_video.cleanString + '.mp4', as_attachment=True)
return render_template('result.html')
<html>
<body>
<h1>Conversion Complete</h1>
<a href='download' target='blank' type='button'><button class='btn btn-default'>Download!</button></a>
</body>
</html>
I am setting up socketio with flask and am having some errors that are preventing it from running.
File "src/gevent/greenlet.py", line 705, in gevent._greenlet.Greenlet.run
File "/Users/hairy/anaconda2/lib/python2.7/site-packages/gevent/baseserver.py", line 26, in _handle_and_close_when_done
return handle(*args_tuple)
File "/Users/hairy/anaconda2/lib/python2.7/site-packages/gevent/server.py", line 193, in wrap_socket_and_handle
ssl_socket.close()
UnboundLocalError: local variable 'ssl_socket' referenced before assignment
2019-07-30T15:07:15Z <Greenlet "Greenlet-0" at 0x102908500: _handle_and_close_when_done(<bound method WSGIServer.wrap_socket_and_handle of, <bound method WSGIServer.do_close of <WSGIServer a, (<socket at 0x1067882d0 fileno=[Errno 9] Bad file )> failed with UnboundLocalError
Here is my flask code:
from flask import Flask, render_template, Markup
from util import getHtml, duplicate, displayArray
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def home():
return displayArray()
#socketio.on('connect')
def test_connect():
emit('after connect', {'data':'Lets dance'})
#socketio.on('plz reload', namespace='/test')
def test_message(message):
emit('reload', {'data': message['data']})
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0', Debug=True)
Here is my js code:
$(document).ready(function(){
var socket = io.connect('http://localhost:5000');
// var socket = io.connect('http://' + document.domain + ':' + location.port + '/test');
// var socket = io.connect('http://localhost:5000');
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'});
});
socket.on('reload', function(msg) {
$('#log').append('<p>Received: ' + msg.data + '</p>');
location.reload();
});
});
The app does not load when I go to localhost:5000
I fixed it by changing
var socket = io.connect('http://localhost:5000');
to
var socket = io.connect();
in the js. I'm not sure why this fixed it, but it did.
For my application I need to inject JS into the loaded url.
I am using the following snippet from here https://stackoverflow.com/a/10866495/1162305
import wx
import wx.html2
class MyBrowser(wx.Dialog):
def __init__(self, *args, **kwds):
wx.Dialog.__init__(self, *args, **kwds)
sizer = wx.BoxSizer(wx.VERTICAL)
self.browser = wx.html2.WebView.New(self)
sizer.Add(self.browser, 1, wx.EXPAND, 10)
self.SetSizer(sizer)
self.SetSize((700, 700))
if __name__ == '__main__':
app = wx.App()
dialog = MyBrowser(None, -1)
dialog.browser.LoadURL("http://www.google.com")
dialog.Show()
dialog.browser.RunScript('alert("hello");')
app.MainLoop()
Here I am injecting javascript with RunScript but the problem is, this javascript loads before the webpage loads. How can I load this javascript after the webpage (given url) loaded completely.
I know in plain javascript I can use document.readyState === "complete", but here how can I do it?
According to documentation:
http://wxpython.org/Phoenix/docs/html/html2.WebView.html#phoenix-title-asynchronous-notifications
You should use EVT_WEBVIEW_LOADED event to check if asynchronous methods like LoadURL is completed.
self.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.browser)
Complete code could look something like (not tested):
import wx
import wx.html2
class MyBrowser(wx.Dialog):
def __init__(self, *args, **kwds):
wx.Dialog.__init__(self, *args, **kwds)
sizer = wx.BoxSizer(wx.VERTICAL)
self.browser = wx.html2.WebView.New(self)
sizer.Add(self.browser, 1, wx.EXPAND, 10)
self.Bind(wx.html2.EVT_WEBVIEW_LOADED, self.OnWebViewLoaded, self.browser)
self.SetSizer(sizer)
self.SetSize((700, 700))
def OnWebViewLoaded(self, evt):
# The full document has loaded
self.browser.RunScript('alert("hello");')
if __name__ == '__main__':
app = wx.App()
dialog = MyBrowser(None, -1)
dialog.browser.LoadURL("http://www.google.com")
dialog.Show()
app.MainLoop()