How can I access view random created folder in flask? - python

I have implement simple app in flask. I can get data and process it also but how can I get random created folder. In this app, I tried to input some data to text area. When export deck button clicked then the data post to flask. I can get data and generate deck also but unable send generated deck file or redirect to the random folder.
I get the following error.
raise TypeError(
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.)
So, How can I implement this? Redirect to new random generated folder or send the generated file as download link.
Thanks
app.py
from flask import Flask, render_template, request, redirect, url_for, flash, send_file, send_from_directory
import image_occ_deck_export
import random, os
app = Flask(__name__)
app.config["CACHE_TYPE"] = "null"
#app.route("/", methods=["GET","POST"])
def home():
if request.method == "POST":
print(request.form['notes'])
notes = request.form['notes']
# random folder
data = request.form['notes']
print(data)
random_f = random.randrange(1 << 30, 1 << 31)
create_random_folder(random_f, data)
else:
return render_template("index.html")
def create_random_folder(random_f, data):
directory = str(random_f)
parent_dir = "static/uploads/"
path = os.path.join(parent_dir, directory)
if not os.path.exists(path):
os.mkdir(path)
random_file = directory + ".txt"
random_deck_name = directory + ".apkg"
file_loc = path + "/" + random_file
deck_loc = path + "/" + random_deck_name
with open(file_loc, 'w') as f:
f.write(str(data))
image_occ_deck_export.exportDeck(file_loc, deck_loc)
return redirect(url_for('uploaded', path=path))
#app.route('/uploaded/<path>', methods=['GET'])
def uploaded():
return render_template("upload.html")
if __name__ == "__main__":
app.run(debug=False)
index.js
function export() {
var textToExport = document.getElementById("noteData").value;
var formData = new FormData();
formData.append("notes", textToExport);
var request = new XMLHttpRequest();
request.open("POST", "/");
request.send(formData);
}
index.html
<html>
<textarea id="noteData"></textarea>
<button onclick="export()">Export Deck</button>
</html>
sample input
cordova-img-occ-note-1602529000819 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529000819.svg'></img> <img src='cordova-img-occ-ans-1602529000819.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img>
cordova-img-occ-note-1602529001248 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529001248.svg'></img> <img src='cordova-img-occ-ans-1602529001248.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img>
cordova-img-occ-note-1602529001673 <img src='art-1851483_640.jpg'></img> <img src='cordova-img-occ-ques-1602529001673.svg'></img> <img src='cordova-img-occ-ans-1602529001673.svg'></img> <img src='cordova-img-occ-orig-1602529000819.svg'></img>
image_occ_deck_export.py
The file use to generate anki deck from txt
import random
import genanki
import csv
import traceback
anki_deck_title = "learn"
anki_model_name = "image occ"
model_id = random.randrange(1 << 30, 1 << 31)
def exportDeck(data_filename, deck_filename):
try:
# front side
front = """
{{#Image}}
<div id="io-header">{{Header}}</div>
<div id="io-wrapper">
<div id="io-overlay">{{Question Mask}}</div>
<div id="io-original">{{Image}}</div>
</div>
<div id="io-footer">{{Footer}}</div>
<script>
// Prevent original image from loading before mask
aFade = 50, qFade = 0;
var mask = document.querySelector('#io-overlay>img');
function loaded() {
var original = document.querySelector('#io-original');
original.style.visibility = "visible";
}
if (mask === null || mask.complete) {
loaded();
} else {
mask.addEventListener('load', loaded);
}
</script>
{{/Image}}
"""
style = """
/* GENERAL CARD STYLE */
.card {
font-family: "Helvetica LT Std", Helvetica, Arial, Sans;
font-size: 150%;
text-align: center;
color: black;
background-color: white;
}
/* OCCLUSION CSS START - don't edit this */
#io-overlay {
position:absolute;
top:0;
width:100%;
z-index:3
}
#io-original {
position:relative;
top:0;
width:100%;
z-index:2;
visibility: hidden;
}
#io-wrapper {
position:relative;
width: 100%;
}
/* OCCLUSION CSS END */
/* OTHER STYLES */
#io-header{
font-size: 1.1em;
margin-bottom: 0.2em;
}
#io-footer{
max-width: 80%;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
font-style: italic;
}
#io-extra-wrapper{
/* the wrapper is needed to center the
left-aligned blocks below it */
width: 80%;
margin-left: auto;
margin-right: auto;
margin-top: 0.5em;
}
#io-extra{
text-align:center;
display: inline-block;
}
.io-extra-entry{
margin-top: 0.8em;
font-size: 0.9em;
text-align:left;
}
.io-field-descr{
margin-bottom: 0.2em;
font-weight: bold;
font-size: 1em;
}
#io-revl-btn {
font-size: 0.5em;
}
/* ADJUSTMENTS FOR MOBILE DEVICES */
.mobile .card, .mobile #content {
font-size: 120%;
margin: 0;
}
.mobile #io-extra-wrapper {
width: 95%;
}
.mobile #io-revl-btn {
font-size: 0.8em;
}
"""
# back side
back = """
{{#Image}}
<div id="io-header">{{Header}}</div>
<div id="io-wrapper">
<div id="io-overlay">{{Answer Mask}}</div>
<div id="io-original">{{Image}}</div>
</div>
{{#Footer}}<div id="io-footer">{{Footer}}</div>{{/Footer}}
<button id="io-revl-btn" onclick="toggle();">Toggle Masks</button>
<div id="io-extra-wrapper">
<div id="io-extra">
{{#Remarks}}
<div class="io-extra-entry">
<div class="io-field-descr">Remarks</div>{{Remarks}}
</div>
{{/Remarks}}
{{#Sources}}
<div class="io-extra-entry">
<div class="io-field-descr">Sources</div>{{Sources}}
</div>
{{/Sources}}
{{#Extra 1}}
<div class="io-extra-entry">
<div class="io-field-descr">Extra 1</div>{{Extra 1}}
</div>
{{/Extra 1}}
{{#Extra 2}}
<div class="io-extra-entry">
<div class="io-field-descr">Extra 2</div>{{Extra 2}}
</div>
{{/Extra 2}}
</div>
</div>
<script>
// Toggle answer mask on clicking the image
var toggle = function() {
var amask = document.getElementById('io-overlay');
if (amask.style.display === 'block' || amask.style.display === '')
amask.style.display = 'none';
else
amask.style.display = 'block'
}
// Prevent original image from loading before mask
aFade = 50, qFade = 0;
var mask = document.querySelector('#io-overlay>img');
function loaded() {
var original = document.querySelector('#io-original');
original.style.visibility = "visible";
}
if (mask === null || mask.complete) {
loaded();
} else {
mask.addEventListener('load', loaded);
}
</script>
{{/Image}}
"""
# print(self.fields)
anki_model = genanki.Model(
model_id,
anki_model_name,
fields=[{"name": "id"},{"name": "Header"}, {"name": "Image"}, {"name": "Question Mask"}, {"name": "Footer"}, {"name": "Remarks"}, {"name": "Sources"}, {"name": "Extra 1"}, {"name": "Extra 2"}, {"name": "Answer Mask"}, {"name": "Original"}],
templates=[
{
"name": "Card 1",
"qfmt": front,
"afmt": back,
},
],
css=style,
)
anki_notes = []
with open(data_filename, "r", encoding="utf-8") as csv_file:
csv_reader = csv.reader(csv_file, delimiter="\t")
for row in csv_reader:
flds = []
for i in range(len(row)):
flds.append(row[i])
anki_note = genanki.Note(
model=anki_model,
fields=flds,
)
anki_notes.append(anki_note)
random.shuffle(anki_notes)
anki_deck = genanki.Deck(model_id, anki_deck_title)
anki_package = genanki.Package(anki_deck)
for anki_note in anki_notes:
anki_deck.add_note(anki_note)
anki_package.write_to_file(deck_filename)
print("Deck generated with {} flashcards".format(
len(anki_deck.notes)))
except Exception:
traceback.print_exc()

create_random_folder() returns a redirect, but when you call it from your home() request handler, you don’t do anything with the returned value and you don’t return a response in that code branch of your home() handler. It seems you intend to return that redirect from your home() handler like so:
return create_random_folder(random_f, data)
Remember, when you return a value from a function, you’re returning the value to the calling code, not to the browser. If you call a function from a request handler and receive a return value, that doesn’t automatically get sent back to the browser; you need to return it from the request handler.

Related

wtf form change style on file input

I am using wtf forms with flask to create a form.
I have a file input, which is styled so the default button is not shown.
How can I dynamically change the style after a file is loaded?
Here is my code:
html:
<div class="file-upload my-form">
<img src="https://i.stack.imgur.com/dy62M.png" />
{{ wtf.form_field(form.file)}}
</div>
css:
.my-form input {
margin: 0;
padding: 0;
height: 100%;
opacity: 0;
}
.file-upload {
margin: 40px auto;
border: 1px solid #149174;
border-radius: 100px;
overflow: hidden;
position: relative;
}
.file-upload input {
position: absolute;
width: 300px;
height: 600px;
left: 10px;
top: 20px;
}
.file-upload img {
height: 170px;
width: 170px;
margin: 60px;
}
how can I change the style on input? or show a label with the file name in the worst case...
In the end I dealt with it like this:
function fileCheck() {
var checked = document.getElementById('file')
if (checked.value.length > 0) {
var file_name = checked.value.split(/(\\|\/)/g).pop();
var re = /(?:\.([^.]+))?$/;
var ext = re.exec(file_name)[1];
if (ext != 'pdf') {
document.getElementById('file-container').style.backgroundColor = '#ca2c2c';
document.getElementById("file-desc").innerHTML = "File Loaded with illegal extension: " + ext + " !!";
document.getElementById("final-submit").disabled = true;
}
else {
document.getElementById('file-container').style.backgroundColor = '#149174';
document.getElementById("file-desc").innerHTML = "File Loaded: " + checked.value.split(/(\\|\/)/g).pop();
document.getElementById("final-submit").disabled = false;
}
}
else {
document.getElementById("final-submit").disabled = true;
}
}
And my html looks like this:
<div class="file-upload my-form" id="file-container">
<img src="static/images/upload_icon.png" />
{{ form.file(oninput="fileCheck()") }}
</div>

How to render a 3D object to HTML file by using pythonOCC in Django?

I have a Django application and I'm using pythonOCC package in it. I have to display the 3D .stl, .stp, .igs files in my template. Normally, when I call the render() function, the following outputs appear on my vscode console and since flask app created by pythonocc instead of django starts running in localhost, my index.html is never rendered. However I need to display the files in a Django template. That's why I have extended the X3DomRenderer Class such as below.
My custom X3DomRenderer class:
class CustomX3DomRenderer(x3dom_renderer.X3DomRenderer):
def render_to_string(self):
self.generate_html_file(self._axes_plane, self._axes_plane_zoom_factor)
return open(self._html_filename, 'r').read()
the HTML codes that returned from render_to_string() function:
<html lang="en">
<head>
<title>pythonOCC 7.4.0 x3dom renderer</title>
<meta name='Author' content='Thomas Paviot - tpaviot#gmail.com'>
<meta name='Keywords' content='WebGl,pythonOCC'>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="https://x3dom.org/release/x3dom.css">
<script src="https://x3dom.org/release/x3dom.js"></script>
<style>
body {
background: linear-gradient(#ced7de, #808080);
margin: 0px;
overflow: hidden;
}
#pythonocc_rocks {
padding: 5px;
position: absolute;
left: 1%;
bottom: 2%;
height: 38px;
width: 280px;
border-radius: 5px;
border: 2px solid #f7941e;
opacity: 0.7;
font-family: Arial;
background-color: #414042;
color: #ffffff;
font-size: 14px;
opacity: 0.5;
}
#commands {
padding: 5px;
position: absolute;
right: 1%;
top: 2%;
height: 65px;
width: 180px;
border-radius: 5px;
border: 2px solid #f7941e;
opacity: 0.7;
font-family: Arial;
background-color: #414042;
color: #ffffff;
font-size: 14px;
opacity: 0.5;
}
a {
color: #f7941e;
text-decoration: none;
}
a:hover {
color: #ffffff;
}
</style>
</head>
<body>
<x3d id="pythonocc-x3d-scene" style="width:100%;border: none" >
<Scene>
<transform scale="1,1,1">
<transform id="plane_smallaxe_Id" rotation="1 0 0 -1.57079632679">
<inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/plane.x3d" mapDEFToID="true" namespaceName="plane"></inline>
<inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/axesSmall.x3d" mapDEFToID="true" namespaceName="axesSmall"></inline>
</transform>
<inline url="https://rawcdn.githack.com/x3dom/component-editor/master/static/x3d/axes.x3d" mapDEFToID="true" namespaceName="axes"></inline>
</transform>
<transform id="glbal_scene_rotation_Id" rotation="1 0 0 -1.57079632679"> <Inline onload="fitCamera()" mapDEFToID="true" url="shp6b8ef6d6e61744489de16a6798cfe998.x3d"></Inline>
</transform> </Scene>
</x3d>
<div id="pythonocc_rocks">
pythonocc-7.4.0 x3dom renderer
<br>Check our blog at
<a href=http://www.pythonocc.org>http://www.pythonocc.org</a>
</div>
<div id="commands">
<b>t</b> view/hide shape<br>
<b>r</b> reset view<br>
<b>a</b> show all<br>
<b>u</b> upright<br>
</div>
<script>
var selected_target_color = null;
var current_selected_shape = null;
var current_mat = null;
function fitCamera()
{
var x3dElem = document.getElementById('pythonocc-x3d-scene');
x3dElem.runtime.fitAll();
}
function select(the_shape) // called whenever a shape is clicked
{
// restore color for previous selected shape
if (current_mat) {
current_mat.diffuseColor = selected_target_color;
}
// store the shape for future process
current_selected_shape = the_shape;
console.log(the_shape);
// store color, to be restored later
appear = current_selected_shape.getElementsByTagName("Appearance")[0];
mat = appear.getElementsByTagName("Material")[0];
current_mat = mat;
console.log(mat);
selected_target_color = mat.diffuseColor;
mat.diffuseColor = "1, 0.65, 0";
//console.log(the_shape.getElementsByTagName("Appearance"));//.getAttribute('diffuseColor'));
}
function onDocumentKeyPress(event) {
event.preventDefault();
if (event.key=="t") { // t key
if (current_selected_shape) {
if (current_selected_shape.render == "true") {
current_selected_shape.render = "false";
}
else {
current_selected_shape.render = "true";
}
}
}
}
// add events
document.addEventListener('keypress', onDocumentKeyPress, false);
</script>
</body>
</html>
and here is my view:
def occ_viewer(request):
shape = read_step_file(os.path.join('C:/Users/imgea/desktop/bgtask/bgtask/ThreeDFile', 'splinecage.stp'))
my_renderer = extend_x3dom.CustomX3DomRenderer(path='C:/Users/imgea/desktop/bgtask/bgtask/ThreeDFile')
my_renderer.DisplayShape(shape)
context = {'viewer': my_renderer.render_to_string()}
return render(request, 'success.html', context)
and I have added these HTML codes that I got from render_to_string() function to my template file. The viewer's grid has shown but the 3D object hasn't because of the error below.
Page not found: http://127.0.0.1:8000/file/occview/shp6b8ef6d6e61744489de16a6798cfe998.x3d
The library creates that .x3d file in the same directory with the file that I wanna render to template but I guess the viewer is looking for this .x3d file in the error which I mentioned before even I sent the directory. I couldn't find the cause of this error. Am I missing something?
Thank you!!

Flask+MySQL. App reloads the same page instead of routing and it fails to add values to mysql db

I know there is much of code below, but i had to show the html/css/js and flask+mysql codes for better understanding. Ask if anything is not clear.
The problem is that, after i give the credentials in the register page and i submit, i expect for it to add the values to database and to route to the login page, but what it does actually, is that it reloads the same page, and the link look like this,
i.e.:
"http://127.0.0.1:5000/register/patient?name_surname=jack+stathem&email=jsm%40outlook.com&ssn=123456789&age=40"
i am having some semantic error and i cant figure out how to fix.
Thank you
var currentTab = 0; // Current tab is set to be the first tab (0)
showTab(currentTab); // Display the current tab
function showTab(n) {
// This function will display the specified tab of the form ...
var x = document.getElementsByClassName("tab");
x[n].style.display = "block";
// ... and fix the Previous/Next buttons:
if (n == 0) {
document.getElementById("prevBtn").style.display = "none";
} else {
document.getElementById("prevBtn").style.display = "inline";
}
if (n == (x.length - 1)) {
document.getElementById("nextBtn").innerHTML = "Submit";
// document.getElementById("nextBtn").value = "submit";
} else {
document.getElementById("nextBtn").innerHTML = "Next";
}
// ... and run a function that displays the correct step indicator:
fixStepIndicator(n)
}
function nextPrev(n) {
// This function will figure out which tab to display
var x = document.getElementsByClassName("tab");
// Exit the function if any field in the current tab is invalid:
if (n == 1 && !validateForm()) return false;
// Hide the current tab:
x[currentTab].style.display = "none";
// Increase or decrease the current tab by 1:
currentTab = currentTab + n;
// if you have reached the end of the form... :
if (currentTab >= x.length) {
//...the form gets submitted:
document.getElementById("regForm").submit();
return false;
}
// Otherwise, display the correct tab:
showTab(currentTab);
}
function validateForm() {
// This function deals with validation of the form fields
var x, y, i, valid = true;
x = document.getElementsByClassName("tab");
y = x[currentTab].getElementsByTagName("input");
// A loop that checks every input field in the current tab:
for (i = 0; i < y.length; i++) {
// If a field is empty...
if (y[i].value == "") {
// add an "invalid" class to the field:
y[i].className += " invalid";
// and set the current valid status to false:
valid = false;
}
}
// If the valid status is true, mark the step as finished and valid:
if (valid) {
document.getElementsByClassName("step")[currentTab].className += " finish";
}
return valid; // return the valid status
}
function fixStepIndicator(n) {
// This function removes the "active" class of all steps...
var i, x = document.getElementsByClassName("step");
for (i = 0; i < x.length; i++) {
x[i].className = x[i].className.replace(" active", "");
}
//... and adds the "active" class to the current step:
x[n].className += " active";
}
/* Style the form */
#regForm {
background-color: #ffffff;
margin: 100px auto;
padding: 40px;
width: 70%;
min-width: 300px;
}
/* Style the input fields */
input {
padding: 10px;
width: 100%;
font-size: 17px;
font-family: Raleway;
border: 1px solid #aaaaaa;
}
/* Mark input boxes that gets an error on validation: */
input.invalid {
background-color: #ffdddd;
}
/* Hide all steps by default: */
.tab {
display: none;
}
/* Make circles that indicate the steps of the form: */
.step {
height: 15px;
width: 15px;
margin: 0 2px;
background-color: #bbbbbb;
border: none;
border-radius: 50%;
display: inline-block;
opacity: 0.5;
}
/* Mark the active step: */
.step.active {
opacity: 1;
}
/* Mark the steps that are finished and valid: */
.step.finish {
background-color: #4CAF50;
}
{% extends 'main.html' %}
{% block head %}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style/docnpatientreg.css') }}">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='bootstrap-tagsinput.css') }}">
{% endblock %}
{% block body %}
<form id="regForm">
<h1>Register:</h1>
<!-- One "tab" for each step in the form: -->
<div class="tab">Name:
<p><input name="name_surname" type="text" placeholder="First name..."></p>
<p><input name='email' type="email" placeholder="E-mail..."></p>
<p><input name='ssn' type="text" placeholder="SSN"></p>
<p><input name='age' type="text" placeholder="Age"></p>
</div>
<div style="overflow:auto;">
<div style="float:right;">
<button type="button" id="prevBtn" onclick="nextPrev(-1)">Previous</button>
<button type="submit" id="nextBtn" onclick="nextPrev(1)">Next</button>
</div>
</div>
<!-- Circles which indicates the steps of the form: -->
<div style="text-align:center;margin-top:40px;">
<span class="step"></span>
</div>
</form>
<script type="text/javascript" src="{{ url_for('static', filename='functionality/docnpatientreg.js') }}"></script>
<script type="text/javascript" src="{{ url_for('static', filename='bootstrap-tagsinput.js') }}"></script>
{% endblock %}
Flask+MySQL
Note: It is not the whole code, if needed i will post it
#medAI.route('/')
def main_base():
cursor.execute("select * from Patients")
result = cursor.fetchall()
print(result)
return render_template('main.html')
#medAI.route('/register/<where>', methods=['GET','POST'])
def register(where):
if where == "patient":
if request.method == "GET":
print("get phase")
return render_template("patientreg.html")
else:
print("post phase")
name_surname=request.form['name_surname']
emailadd=request.form['email']
# weight=request.form['weight']
# height=request.form['']
ssn=request.form['ssn']
age=request.form['age']
# gender=request.form['gender']
# symptoms=request.form['symptoms']
cursor.execute('select * from Patients')
patients = cursor.fetchall()
print(patients)
check = True
for patient in patients:
print(patient[0])
if check == True:
cursor.execute("insert into patients(name_surname,emailadd, ssn)"
"values(%s);",(name_surname,emailadd, ssn, age,))
database.commit()
print('done')
else:
print('failed')
return redirect('/register/patient')
return redirect('/login/')
elif where == "doctor":
pass
return "Invalid Registration Attempt"
"action" and "method" are missing in the form.
<form id="regForm" action="/register/patient" method="POST">

Python HTML Webchat with mysql database entries

I'm building a webchat application in python. Basically I have all the messages stored in a MySQL DB and I need to have them populate in the chat one by one.
Here is the DB:
Here I generate HTML Template:
class MyHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Sending an '200 OK' response
self.send_response(200)
self.send_header("Content-type", "text/html")
# Whenever using 'send_header', you also have to call 'end_headers'
self.end_headers()
query_components = parse_qs(urlparse(self.path).query)
if 'mymessage' in query_components:
MyMessage = query_components["mymessage"][0]
send_message = f'INSERT INTO lora_messages (mymessages, yourmessages) VALUES ("{MyMessage}", "")'
insertmsg.execute(send_message)
# Some custom HTML code, possibly generated by another function
styler = """
body {
font-family: helvetica;
display: flex ;
flex-direction: column;
align-items: center;
}
input[type=text] {
width: auto;
}
.chat {
-ms-transform: scale(1);
-webkit-transform: scale(1);
-moz-transform: scale(1);
-o-transform: scale(1);
transform: scale(1);
}
.messages {
margin-top: 30px;
display: flex;
flex-direction: column;
}
.message {
border-radius: 20px;
padding: 8px 15px;
margin-top: 5px;
margin-bottom: 5px;
display: inline-block;
}
.yours {
align-items: flex-start;
}
.yours .message {
margin-right: 25%;
background-color: #eee;
position: relative;
}
.mine {
align-items: flex-end;
}
.mine .message {
color: white;
margin-left: 25%;
background: linear-gradient(to bottom, #00D0EA 0%, #0085D1 100%);
background-attachment: fixed;
position: relative;
} """
html = f"""
<html>
<head>
<style>{styler}</style>
</head>
<h1>LoRa Chat</h1>
{mymessagetemplate}
{yourmessagetemplate}
<form action="" Method="GET">
<input type="text" id="body" name="mymessage">
<input type="submit" value="Submit">
</form>"""
# Writing the HTML contents with UTF-8
self.wfile.write(bytes(html, "utf8"))
return
# Create an object of the above class
handler_object = MyHttpRequestHandler
PORT = 8035
my_server = socketserver.TCPServer(("", PORT), handler_object)
my_server.allow_reuse_address = True
# Start the server
my_server.serve_forever()
Here I'm writing the while loop and assigning it to the variable that I added to the HTML template {yourmessagetemplate} and {mymessagetemplate}.
import http.server
import socketserver
import mysql.connector
from urllib.parse import urlparse
from urllib.parse import parse_qs
mydb = mysql.connector.connect(
host="localhost",
user="user",
passwd="pass",
database="allmessages",
autocommit=True
)
yourmess = mydb.cursor()
mymess = mydb.cursor()
insertmsg = mydb.cursor()
yourmess.execute("SELECT yourmessages FROM lora_messages WHERE yourmessages != ''")
yourmsg = yourmess.fetchone()
while yourmsg is not None:
yourmsg = yourmess.fetchone()
yourmessagetemplate = f"<div class='chat'><div class='yours messages'><div class='message'>{yourmsg}</div></div>"
mymess.execute("SELECT mymessages FROM lora_messages WHERE mymessages != ''")
mymsg = mymess.fetchone()
while mymsg is not None:
mymsg = mymess.fetchone()
mymessagetemplate = f"<div class='chat'><div class='mine messages'><div class='message'>{mymsg}</div></div>"
How can I get this program to write each line one by one? The result I get is that {yourmessagetemplate} only shows the last message in the DB which is "None" for both sides of the conversation and omits the rest of the messages.
The code is overwriting the message templates in each iteration of the while loops. You probably want to collect each template in a list, and then join them together once they have all been collected.
# Create a list to hold templates
yourmessagetemplates = []
yourmess.execute("SELECT yourmessages FROM lora_messages WHERE yourmessages != ''")
yourmsg = yourmess.fetchone()
while yourmsg is not None:
yourmsg = yourmess.fetchone()
yourmessagetemplates.append(f"<div class='chat'><div class='yours messages'><div class='message'>{yourmsg}</div></div>")
# Join the individual templates into a single piece of html.
yourmessagetemplate = '<br>'.join(yourmessagetemplates)
# Create a list to hold templates
mymessagetemplates = []
mymess.execute("SELECT mymessages FROM lora_messages WHERE mymessages != ''")
mymsg = mymess.fetchone()
while mymsg is not None:
mymsg = mymess.fetchone()
mymessagetemplates.append(f"<div class='chat'><div class='mine messages'><div class='message'>{mymsg}</div></div>")
# Join the individual templates into a single piece of html.
mymessagetemplate = '<br>'.join(mymessagetemplates)

How to open download file dialog with QWebEngineView?

I'm building a pyqt5 desktop interface where I'm using QWebEngineView to show a html file where I show a Leaflet map. This is working fine.
The next step is to export all features the user added to the map. When I click on "Export Features" on the map nothing happen, but when I open the same html file on my Chromium web browser, the "Export feature" opens the download dialog fine.
PyQt5 script:
self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
self.html_path = os.path.split(os.path.abspath(__file__))[0] + r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<title>FazMaraneyRGB_transparent_mosaic_group1</title>
<!-- Leaflet -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>
<!-- Leaflet.draw -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.css" />
<script src="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.js"></script>
<!-- Leaflet Ajax -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
<!-- Leaflet Measument -->
<link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
<script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>
<style>
body { margin:0; padding:0; }
body, table, tr, td, th, div, h1, h2, input { font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif; font-size: 11pt; }
#map { position:absolute; top:0; bottom:0; width:100%; } /* full size */
.ctl {
padding: 2px 10px 2px 10px;
background: white;
background: rgba(255,255,255,0.9);
box-shadow: 0 0 15px rgba(0,0,0,0.2);
border-radius: 5px;
text-align: right;
}
.title {
font-size: 18pt;
font-weight: bold;
}
.src {
font-size: 10pt;
}
#delete, #export {
position: absolute;
top:100px;
right:10px;
z-index:100;
background:white;
color:black;
padding:6px;
border-radius:4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size:12px;
text-decoration:none;
}
#export {
top:130px;
}
</style>
</head>
<body>
<div id='map'></div>
<div id='delete'>Delete Features</div>
<a href='#' id='export'>Export Features</a>
<script>
/* **** Leaflet **** */
// Base layers
// .. OpenStreetMap
var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {attribution: '© OpenStreetMap contributors'});
// .. White background
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
// Overlay layers (TMS)
var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {tms: true, maxZoom: 22, opacity: 0.9, attribution: ""});
// Map
var map = L.map('map', {
measureControl: true,
center: [-18.3604868606589, -52.694255477616245],
zoom: 22,
minZoom: 0,
maxZoom: 22,
layers: [osm]
});
lyr1.addTo(map);
//Geojson Layers
var basemaps = {"OpenStreetMap": osm, "Without background": white}
var overlaymaps = {"Layer 1": lyr1}
// Title
var title = L.control();
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl title');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
};
title.addTo(map);
// Note
var src = 'Generated by Hawkit';
var title = L.control({position: 'bottomleft'});
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl src');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = src;
};
title.addTo(map);
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function(e) {
// Each time a feaute is created, it's added to the over arching feature group
featureGroup.addLayer(e.layer);
});
// on click, clear all layers
document.getElementById('delete').onclick = function(e) {
featureGroup.clearLayers();
}
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download','data.geojson');
}
// Add base layers
L.control.layers(basemaps, overlaymaps, {collapsed: true}).addTo(map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
map.fitBounds([[-18.36827062251916, -52.6871074784942], [-18.35270287637126, -52.7014028427423]]);
</script>
</body>
</html>
That popup window is generated by the browser, in the case of QWebEngine we must create it. To start, the signal indicating the download must be detected, and this signal is downloadRequested from the QWebEngineProfile. That signal sends us a QWebEngineDownloadItem object that handles the download, in it we create a dialog window with the help of QFileDialog. For this case we will create a custom QWebEnginePage as shown below:
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' />
<title>FazMaraneyRGB_transparent_mosaic_group1</title>
<!-- Leaflet -->
<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.css" />
<script src="http://cdn.leafletjs.com/leaflet-0.7.5/leaflet.js"></script>
<!-- Leaflet.draw -->
<link rel="stylesheet" href="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.css" />
<script src="https://unpkg.com/leaflet-draw#0.4.1/dist/leaflet.draw.js"></script>
<!-- Leaflet Ajax -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-ajax/2.1.0/leaflet.ajax.min.js"></script>
<!-- Leaflet Measument -->
<link rel="stylesheet" href="http://ljagis.github.io/leaflet-measure/leaflet-measure.css" />
<script src="http://ljagis.github.io/leaflet-measure/leaflet-measure.min.js"></script>
<style>
body {
margin: 0;
padding: 0;
}
body,
table,
tr,
td,
th,
div,
h1,
h2,
input {
font-family: "Calibri", "Trebuchet MS", "Ubuntu", Serif;
font-size: 11pt;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
/* full size */
.ctl {
padding: 2px 10px 2px 10px;
background: white;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
border-radius: 5px;
text-align: right;
}
.title {
font-size: 18pt;
font-weight: bold;
}
.src {
font-size: 10pt;
}
#delete,
#export {
position: absolute;
top: 100px;
right: 10px;
z-index: 100;
background: white;
color: black;
padding: 6px;
border-radius: 4px;
font-family: 'Helvetica Neue';
cursor: pointer;
font-size: 12px;
text-decoration: none;
}
#export {
top: 130px;
}
</style>
</head>
<body>
<div id='map'></div>
<div id='delete'>Delete Features</div>
<a href='#' id='export'>Export Features</a>
<script>
/* **** Leaflet **** */
// Base layers
// .. OpenStreetMap
var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
});
// .. White background
var white = L.tileLayer("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEX///+nxBvIAAAAH0lEQVQYGe3BAQ0AAADCIPunfg43YAAAAAAAAAAA5wIhAAAB9aK9BAAAAABJRU5ErkJggg==");
// Overlay layers (TMS)
var lyr1 = L.tileLayer('./tiles/{z}/{x}/{y}.png', {
tms: true,
maxZoom: 22,
opacity: 0.9,
attribution: ""
});
// Map
var map = L.map('map', {
measureControl: true,
center: [-18.3604868606589, -52.694255477616245],
zoom: 22,
minZoom: 0,
maxZoom: 22,
layers: [osm]
});
lyr1.addTo(map);
//Geojson Layers
var basemaps = {
"OpenStreetMap": osm,
"Without background": white
}
var overlaymaps = {
"Layer 1": lyr1
}
// Title
var title = L.control();
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl title');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = "FazMaraneyRGB_transparent_mosaic_group1";
};
title.addTo(map);
// Note
var src = 'Generated by Hawkit';
var title = L.control({
position: 'bottomleft'
});
title.onAdd = function(map) {
this._div = L.DomUtil.create('div', 'ctl src');
this.update();
return this._div;
};
title.update = function(props) {
this._div.innerHTML = src;
};
title.addTo(map);
var featureGroup = L.featureGroup().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: featureGroup
}
}).addTo(map);
map.on('draw:created', function(e) {
// Each time a feaute is created, it's added to the over arching feature group
featureGroup.addLayer(e.layer);
});
// on click, clear all layers
document.getElementById('delete').onclick = function(e) {
featureGroup.clearLayers();
}
document.getElementById('export').onclick = function(e) {
// Extract GeoJson from featureGroup
var data = featureGroup.toGeoJSON();
// Stringify the GeoJson
var convertedData = 'text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(data));
// Create export
document.getElementById('export').setAttribute('href', 'data:' + convertedData);
document.getElementById('export').setAttribute('download', 'data.geojson');
}
// Add base layers
L.control.layers(basemaps, overlaymaps, {
collapsed: true
}).addTo(map);
// Fit to overlay bounds (SW and NE points with (lat, lon))
map.fitBounds([
[-18.36827062251916, -52.6871074784942],
[-18.35270287637126, -52.7014028427423]
]);
</script>
</body>
</html>
main.py
from PyQt5 import QtWebEngineWidgets, QtWidgets, QtCore
class WebEnginePage(QtWebEngineWidgets.QWebEnginePage):
def __init__(self, *args, **kwargs):
QtWebEngineWidgets.QWebEnginePage.__init__(self, *args, **kwargs)
self.profile().downloadRequested.connect(self.on_downloadRequested)
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def on_downloadRequested(self, download):
old_path = download.path()
suffix = QtCore.QFileInfo(old_path).suffix()
path, _ = QtWidgets.QFileDialog.getSaveFileName(self.view(), "Save File", old_path, "*."+suffix)
if path:
download.setPath(path)
download.accept()
if __name__ == '__main__':
import sys
sys.argv.append("--remote-debugging-port=8000")
sys.argv.append("--disable-web-security")
app = QtWidgets.QApplication(sys.argv)
view = QtWebEngineWidgets.QWebEngineView()
page = WebEnginePage(view)
view.setPage(page)
path = QtCore.QDir.current().filePath("index.html")
view.load(QtCore.QUrl.fromLocalFile(path))
view.show()
sys.exit(app.exec_())
In your case:
self.MainWindow.webMapViewer = QtWebEngineWidgets.QWebEngineView()
self.MainWindow.webPageLayout.addWidget(self.MainWindow.webMapViewer)
page = WebEnginePage(self.MainWindow.webMapViewer) # create page
self.MainWindow.webMapViewer.setPage(page) # set page
self.html_path = os.path.split(os.path.abspath(__file__))[0] + r'/html/test.html'
self.MainWindow.webMapViewer.load(QtCore.QUrl().fromLocalFile(self.html_path))
Plus:
If you want to set a defined route use the following:
#QtCore.pyqtSlot(QtWebEngineWidgets.QWebEngineDownloadItem)
def on_downloadRequested(self, download):
directory = "/path/of/directory"
filename = QtCore.QFileInfo(download.path()).fileName()
download.setPath(QtCore.QDir(directory).filePath(filename))
download.accept()

Categories