I am not familiar with javascript and QWebEnginePage.
When i set self.content.setText('text') content, the QWebEngineView content not changed?
The python code
class Document(QObject):
textChanged = pyqtSignal(str)
def __init__(self):
super().__init__()
self.text = ''
def setText(self, text):
self.text = text
self.textChanged.emit(text)
print('emit')
class Demo(QWebEngineView):
def __init__(self):
super().__init__()
self.content = Document()
page = self.page()
channel = QWebChannel()
channel.registerObject('content', self.content)
page.setWebChannel(channel)
with open('index.html') as f:
self.setHtml(f.read())
self.content.setText('text')
app = QApplication([])
demo = Demo()
demo.resize(500, 400)
demo.show()
app.exec()
The index html:
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<script src="qwebchannel.js"></script>
</head>
<body>
<div id="placeholder">22</div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var updateText = function (text) {
placeholder.innerHTML = text;
console.log(text);
}
new QWebChannel(qt.webChannelTransport,
function (channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
</script>
</body>
</html>
You have the following errors:
channel is a local variable that will be removed as soon as the "Demo" constructor is finished, and that is the intermediary between Python and Javascript communication. The solution is to extend the life cycle by passing it a parent (Qt style) or make it an attribute of the class.
In the .html you are trying to include qwebchannel.js but in general you should use <script src="qrc:///qtwebchannel/qwebchannel.js"></script>.
If you export a QObject to javascript then only the Q-Properties, QSlot and QSignals will be exported since QWebChannel uses the QMetaObject instropection, but "text" is neither of them so it will be undefined in javascript. The solution is to expose it as a pyqtProperty.
import os
from PyQt5.QtCore import pyqtProperty, pyqtSignal, QObject, QUrl
from PyQt5.QtWidgets import QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtWebChannel import QWebChannel
CURRENT_DIR = os.path.dirname(os.path.realpath(__file__))
class Document(QObject):
textChanged = pyqtSignal(str)
def __init__(self):
super().__init__()
self._text = ""
def text(self):
return self._text
def setText(self, text):
self._text = text
self.textChanged.emit(text)
print("emit")
text = pyqtProperty(str, fget=text, fset=setText, notify=textChanged)
class Demo(QWebEngineView):
def __init__(self):
super().__init__()
self.content = Document()
channel = QWebChannel(self)
channel.registerObject("content", self.content)
self.page().setWebChannel(channel)
filename = os.path.join(CURRENT_DIR, "index.html")
self.load(QUrl.fromLocalFile(filename))
self.content.setText("text")
def main():
app = QApplication([])
demo = Demo()
demo.resize(500, 400)
demo.show()
app.exec()
if __name__ == "__main__":
main()
<!doctype html>
<html lang="en">
<meta charset="utf-8">
<head>
<script src="qrc:///qtwebchannel/qwebchannel.js"></script>
</head>
<body>
<div id="placeholder">22</div>
<script>
'use strict';
var placeholder = document.getElementById('placeholder');
var updateText = function (text) {
placeholder.innerHTML = text;
console.log(text);
}
new QWebChannel(qt.webChannelTransport,
function (channel) {
var content = channel.objects.content;
updateText(content.text);
content.textChanged.connect(updateText);
}
);
</script>
</body>
</html>
Related
I'm finishing an application by django that generates pdf certificates
it was working great before I pull it into heroke and set up S3 amazom to host static files.
I have an html with the certificate template, and by HTML2PDF I render it to pdf. But it's not showing the background by css, it works just in the tag
Weird that if we opent the image url in s3 amazom it's shown perfectly
here the template css part
<meta charset="utf-8" />
{% load static %}
<style type="text/css">
#page {
size: 1122.52px 1587.4px ;
/*size: A4 landscape;*/
margin: 0cm;
background-image: url({{bg_front}});
height: 1588;
}
</style>
My view:
class ViewPDF(View):
def get(self, request, *args, **kwargs):
data = {}
pdf = True
if kwargs['pk']:
try:
participant = Participant.objects.get(pk=kwargs['pk'])
print(participant.cpf)
if participant.name:
certificate = Certificate.objects.get(pk=participant.certificate.pk)
pathBack = str(certificate.template.template_back.url)
pathFront = str(certificate.template.template_front.url)
print(pathFront)
#
# CONFIGURA OS BACKGROUNDS E TEXTO
#
data['bg_front'] = pathFront
data['bg_back'] = pathBack
setting = certificate.template.settings
start_date = datetime.strftime(certificate.start_date,'%d/%m/%Y')
end_date = datetime.strftime(certificate.start_date,'%d/%m/%Y')
data['text_front'] = setting.replace('<<nome>>',participant.name).replace('<<cpf>>',str(participant.cpf)).replace('<<ch>>',str(certificate.ch)).replace('<<instituicao>>',str(certificate.institution)).replace('<<DataInicio>>',start_date).replace('<<DataFim>>',end_date)
data['cpf'] = participant.cpf
pdf = render_to_pdf('app_certificates/body_front_pdf.html', data)
return HttpResponse(pdf, content_type='application/pdf')
except TypeError as e:
return HttpResponse(e)
I've built a simple web server that gets a request and send a response. So when the server gets an invalid request, like "localhost/not-a-page", the server will send a response with the content of the HTML file "404.html" the webpage should display an image. So far, so good.
But when the 404 page loads up, the page can't find the image. The HTML part is correct and works offline. I've tried to move the image to serval locations, relative to the Python script, relative to the HTML. But it just can't find it. Hi I'm trying to make the server as low-level as I can, I want to learn how servers work. So I'm not using any server-related libraries. I'm using only the socket library of Python.
I'll appreciate any help to resolve this problem without using other libraries,
EDIT
Here is the relevant Python part :
import socket
import threading
import os
default_error_page = """\
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>Error response</title>
</head>
<body>
<center>
<h1>Response</h1>
<p>Error code: %(code)d</p>
<p>Message: %(status)s.</p>
</center>
</body>
</html>
"""
default_header_status = "HTTP/1.1 %(code)d %(status)s\r\n"
default_header_content_type = "Content-Type: text/html; charset=utf-8\r\n\r\n"
buffer_size = 1024
def get_page(code):
page = default_error_page
if code == 200:
pass
else:
file = open(os.path.dirname(__file__) + "/www/not-found.html", 'r')
page = file.read()
return page
class BaseServer:
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_name = ""
host_name = ""
host_port = 8000 # default port
is_shutdown = False
def __init__(self, name):
self.server_name = name
def start_server(self):
thread = threading.Thread(target=self.run_server(), name=self.server_name)
thread.start()
def run_server(self):
self.server_socket.bind((self.host_name, self.host_port)) # bind to host
self.server_socket.listen()
while not self.is_shutdown:
conn, addr = self.server_socket.accept()
self.handle_request(conn, addr)
def handle_request(self, connection, address):
print(str(address[0]) + " Connected! (port " + str(address[1]) + ")")
result = self.parse_request(connection.recv(buffer_size))
if result == 0:
page = self.parse_response(200)
else:
page = self.parse_response(404)
connection.sendall(bytes(page))
def parse_request(self, data):
if len(data) == 0:
return
strings = str(bytes(data).decode('utf-8')).split('\r\n')
command, path, version = strings[0].split()
print("command - " + command)
print("path - " + path)
print("version - " + version)
status = 1
if path == "/":
status = 0
return status
def parse_response(self, code):
status = "ERROR"
if code == 200:
status = "OK"
elif code == 404:
status = "NOT FOUND"
base_header = (default_header_status % {'code': code, 'status': status})
base_content_type = default_header_content_type
# page = (default_error_page % {'code': code, 'status': status})
page = str(get_page(code))
return_string = str(base_header + base_content_type + page).encode('utf-8')
print(return_string)
return return_string
def main():
server = BaseServer("Home Server")
server.start_server()
if __name__ == "__main__":
main()
And this is the HTML:
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style/main.css"/>
<style>
*{
padding:0;
margin:0;
}
body{
background-color:#ffe6b3;
}
h1{
margin-top:30px;
background-color:#ffcc66;
font-size:3em;
display:inline-block;
color:#3a0000;
}
p{
margin-top:80px;
font-size:2em;
color:#3a0000;
}
#img404{
background-image:url(../images/404.gif);
width:100%;
height:50%;
background-repeat:no-repeat;
background-position:center 20%;
}
</style>
</head>
<body>
<center>
<div class=top>
<h1>ERROR 404</h1>
</div>
<p>
Sorry, we could not find the page :(
</p>
<div id="img404">
</div>
</center>
</body>
</html>
Sorry if it's not very readable, but I'm on the phone.
Dima.
Don't use relative paths for image like ../images/img.gif. Rather use full url or url relative to the root.
http://localhost/images/img.gif - full url
/images/img.gif - path relative to root url
Figured out my problem.
After I saw the logs, I realized that the browser sends another request, for the image.
And silly me, my code is:
if path ="/":
status = 0
else:
status = 1
So for every request which is not root("/") the server will return 404.
Oops
I have configured xampp on windows to work with python 2.7 and Pygments. My php code is highlighted properly in Pygments on the website. The code has colors, span elements, classes.
That is how it looks:
But I cannot get line numbers.
As I have read tutorials it depends on the linenos value in python script. The value should be either table or inline or 1 or True.
But it does not work for me. I still gives the same final code
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="gh.css">
</head>
<body>
<div class="highlight highlight-php"><pre><code><span class="nv">$name</span> <span class="o">=</span> <span class="s2">"Jaś"</span><span class="p">;</span>
<span class="k">echo</span> <span class="s2">"Zażółć gęślą jaźń, "</span> <span class="o">.</span> <span class="nv">$name</span> <span class="o">.</span> <span class="s1">'.'</span><span class="p">;</span>
<span class="k">echo</span> <span class="s2">"hehehe#jo.io"</span><span class="p">;</span>
</code></pre></div>
</html>
How to add line numbers? I put two files of the website below:
index.py
import sys
from pygments import highlight
from pygments.formatters import HtmlFormatter
# If there isn't only 2 args something weird is going on
expecting = 2;
if ( len(sys.argv) != expecting + 1 ):
exit(128)
# Get the code
language = (sys.argv[1]).lower()
filename = sys.argv[2]
f = open(filename, 'rb')
code = f.read()
f.close()
# PHP
if language == 'php':
from pygments.lexers import PhpLexer
lexer = PhpLexer(startinline=True)
# GUESS
elif language == 'guess':
from pygments.lexers import guess_lexer
lexer = guess_lexer( code )
# GET BY NAME
else:
from pygments.lexers import get_lexer_by_name
lexer = get_lexer_by_name( language )
# OUTPUT
formatter = HtmlFormatter(linenos='table', encoding='utf-8', nowrap=True)
highlighted = highlight(code, lexer, formatter)
print highlighted
index.php
<?php
define('MB_WPP_BASE', dirname(__FILE__));
function mb_pygments_convert_code($matches)
{
$pygments_build = MB_WPP_BASE . '/index.py';
$source_code = isset($matches[3]) ? $matches[3] : '';
$class_name = isset($matches[2]) ? $matches[2] : '';
// Creates a temporary filename
$temp_file = tempnam(sys_get_temp_dir(), 'MB_Pygments_');
// Populate temporary file
$filehandle = fopen($temp_file, "w");
fwrite($filehandle, html_entity_decode($source_code, ENT_COMPAT, 'UTF-8'));
fclose($filehandle);
// Creates pygments command
$language = $class_name ? $class_name : 'guess';
$command = sprintf('C:\Python27/python %s %s %s', $pygments_build, $language, $temp_file);
// Executes the command
$retVal = -1;
exec($command, $output, $retVal);
unlink($temp_file);
// Returns Source Code
$format = '<div class="highlight highlight-%s"><pre><code>%s</code></pre></div>';
if ($retVal == 0)
$source_code = implode("\n", $output);
$highlighted_code = sprintf($format, $language, $source_code);
return $highlighted_code;
}
// This prevent throwing error
libxml_use_internal_errors(true);
// Get all pre from post content
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding('
<pre class="php">
<code>
$name = "Jaś";
echo "Zażółć gęślą jaźń, " . $name . \'.\';
echo "<address>hehehe#jo.io</address>";
</code>
</pre>', 'HTML-ENTITIES', "UTF-8"), LIBXML_HTML_NODEFDTD);
$pres = $dom->getElementsByTagName('pre');
foreach ($pres as $pre) {
$class = $pre->attributes->getNamedItem('class')->nodeValue;
$code = $pre->nodeValue;
$args = array(
2 => $class, // Element at position [2] is the class
3 => $code // And element at position [2] is the code
);
// convert the code
$new_code = mb_pygments_convert_code($args);
// Replace the actual pre with the new one.
$new_pre = $dom->createDocumentFragment();
$new_pre->appendXML($new_code);
$pre->parentNode->replaceChild($new_pre, $pre);
}
// Save the HTML of the new code.
$newHtml = "";
foreach ($dom->getElementsByTagName('body')->item(0)->childNodes as $child) {
$newHtml .= $dom->saveHTML($child);
}
?>
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="gh.css">
</head>
<body>
<?= $newHtml ?>
</body>
</html>
Thank you
While reading the file try readlines:
f = open(filename, 'rb')
code = f.readlines()
f.close()
This way you do the following it will get multiple lines :
formatter = HtmlFormatter(linenos='table', encoding='utf-8', nowrap=True)
Suggestion:
More pythonic way of opening files is :
with open(filename, 'rb') as f:
code = f.readlines()
That's it python context manager closes this file for you.
Solved!
nowrap
If set to True, don’t wrap the tokens at all, not even inside a tag. This disables most other options (default: False).
http://pygments.org/docs/formatters/#HtmlFormatter
I am trying to build a simple REST server with python SimpleHTTPServer. I am having problem reading data from the post message. Please let me know if I am doing it right.
from SimpleHTTPServer import SimpleHTTPRequestHandler
import SocketServer
import simplejson
class S(SimpleHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
print "got get request %s" % (self.path)
if self.path == '/':
self.path = '/index.html'
return SimpleHTTPRequestHandler.do_GET(self)
def do_POST(self):
print "got post!!"
content_len = int(self.headers.getheader('content-length', 0))
post_body = self.rfile.read(content_len)
test_data = simplejson.loads(post_body)
print "post_body(%s)" % (test_data)
return SimpleHTTPRequestHandler.do_POST(self)
def run(handler_class=S, port=80):
httpd = SocketServer.TCPServer(("", port), handler_class)
print 'Starting httpd...'
httpd.serve_forever()
The index.html file
<html>
<title>JSON TEST PAGE</title>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
JSONTest = function() {
var resultDiv = $("#resultDivContainer");
$.ajax({
url: "http://128.107.138.51:8080",
type: "POST",
data: {txt1: $("#json_text").val()},
dataType: "json",
success: function (result) {
switch (result) {
case true:
processResponse(result);
break;
default:
resultDiv.html(result);
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
};
</script>
</head>
<body>
<h1>My Web Page</h1>
<div id="resultDivContainer"></div>
<form>
<textarea name="json_text" id="json_text" rows="50" cols="80">
[{"resources": {"dut": "any_ts", "endpoint1": "endpoint", "endpoint2": "endpoint"}},
{"action": "create_conference", "serverName": "dut", "confName": "GURU_TEST"}]
</textarea>
<button type="button" onclick="JSONTest()">Generate Test</button>
</form>
</body>
</html>
The SimpleJson fails to load the json from the POST message. I am not familiar with web coding and I am not even sure if what I am doing is right for creating a simple REST API server.
I appreciate your help.
Thanks matthewatabet for the klein idea. I figured a way to implement it using BaseHTTPHandler. The code below.
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer
import simplejson
import random
class S(BaseHTTPRequestHandler):
def _set_headers(self):
self.send_response(200)
self.send_header('Content-type', 'text/html')
self.end_headers()
def do_GET(self):
self._set_headers()
f = open("index.html", "r")
self.wfile.write(f.read())
def do_HEAD(self):
self._set_headers()
def do_POST(self):
self._set_headers()
print "in post method"
self.data_string = self.rfile.read(int(self.headers['Content-Length']))
self.send_response(200)
self.end_headers()
data = simplejson.loads(self.data_string)
with open("test123456.json", "w") as outfile:
simplejson.dump(data, outfile)
print "{}".format(data)
f = open("for_presen.py")
self.wfile.write(f.read())
return
def run(server_class=HTTPServer, handler_class=S, port=80):
server_address = ('', port)
httpd = server_class(server_address, handler_class)
print 'Starting httpd...'
httpd.serve_forever()
if __name__ == "__main__":
from sys import argv
if len(argv) == 2:
run(port=int(argv[1]))
else:
run()
And the corresponding html page
<form action="/profile/index/sendmessage" method="post" enctype="application/x-www-form-urlencoded">
<div class="upload_form">
<dt id="message-label"><label class="optional" for="message">Enter Message</label></dt>
<dd id="message-element">
<textarea cols="80" rows="50" id="message" name="message">
[{"resources": {"dut": "any_ts", "endpoint1": "multistream_endpoint", "endpoint2": "multistream_endpoint"}},
{"action": "create_conference", "serverName": "dut", "conferenceName": "GURU_SLAVE_TS"},
{"action": "dial_out_ep", "serverName": "dut", "confName": "GURU_SLAVE_TS", "epName": "endpoint1"}
]
</textarea></dd>
<dt id="id-label"> </dt>
<dd id="id-element">
<input type="hidden" id="id" value="145198" name="id"></dd>
<dt id="send_message-label"> </dt>
<dd id="send_message-element">
<input type="submit" class="sendamessage" value="Send" id="send_message" name="send_message"></dd>
</div>
</form>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript">
$("input.sendamessage").click(function(event) {
event.preventDefault();
var message = $('textarea#message').val();
var id = $('input#id').val();
url = "http://128.107.138.51:8080"
var posting = $.post(url, message)
posting.done(function( data ) {
alert(message);
});
});
</script>
SimpleHTTPRequestHandler does not support POST. It's very simple indeed. Check out Klein whose server is a bit more full-featured.
There's an example of a json PUT (pretty close to POST) here:
https://pypi.python.org/pypi/klein/0.2.3
import json
from klein import Klein
class ItemStore(object):
app = Klein()
def __init__(self):
self._items = {}
#app.route('/')
def items(self, request):
request.setHeader('Content-Type', 'application/json')
return json.dumps(self._items)
#app.route('/<string:name>', methods=['PUT'])
def save_item(self, request, name):
request.setHeader('Content-Type', 'application/json')
body = json.loads(request.content.read())
self._items[name] = body
return json.dumps({'success': True})
#app.route('/<string:name>', methods=['GET'])
def get_item(self, request, name):
request.setHeader('Content-Type', 'application/json')
return json.dumps(self._items.get(name))
if __name__ == '__main__':
store = ItemStore()
store.app.run('localhost', 8080)
I want to show OpenCV processed image with web interface (made with CherryPy). Code below works fine:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
cherrypy.response.headers['Content-Type'] = 'image/jpeg'
return data.tostring()
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
However: I would like to embed the image in html so I can (for example) add another image or other data to the same page.
I have tried the following code:
import cherrypy
import cv2
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpeg', image)
return """ <html>
<head>
<title>CherryPy static imagl</title>
</head>
<html>
<body>
<img src=" """ + data + """:image/jpeg" />
</body>
</html>"""
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
But this gives the following error:
<img src=" """ + data + """:image/jpeg" />
TypeError: cannot concatenate 'str' and 'numpy.ndarray' objects
Converting the numpy arry to a string using the following code also does not work (it gives no error but displays only characters):
<img src=" """ + data.tostring() + """:image/jpeg" />
Anyone who can give me some more insight? Thanks in advance!
The following code does the trick :)
import cherrypy
import cv2
import base64
class Picture(object):
def __init__(self):
self.cam = cv2.VideoCapture(0)
#cherrypy.expose
def index(self):
_, image = self.cam.read()
_, data = cv2.imencode('.jpg', image)
jpeg_base64 = base64.b64encode(data.tostring())
return """
<html>
<head>
<meta http-equiv="refresh" content="1" />
<title>Cherrypy webcam</title>
</head>
<html>
<body>
<img src='data:image/jpeg;base64,%s' />
<img src='data:image/jpeg;base64,%s' />
</body>
</html>
""" % (jpeg_base64, jpeg_base64)
if __name__ == '__main__':
cherrypy.config.update({'server.socket_host': '127.0.0.1', 'server.socket_port': 80, })
cherrypy.quickstart(Picture())
This code displays the same picture two times. and the:
<meta http-equiv="refresh" content="1" />
refreshes the code every second.