Timer in python with flask - python

I am trying to display a timer of 5minutes (for example). I am using flask.
I know it could be good to use javascript but I really want to do it with python.
I have two issues:
First issue: display of the timer - issue to overwrite
I wrote a function for the timer in python which is supposed to display (for example for 50 seconds):
00:50 then remove 00:50 and have00:49, and so on...
But it is displaying:
00:50
00:49
00:48
...
Here is my code: screen.py
from flask import Flask, Response, request, render_template, render_template_string, stream_with_context
import time
app = Flask(__name__)
timing=0
#app.route('/content', methods=['POST', 'GET']) # render the content a url differnt from index. This will be streamed into the iframe
def content():
global timing
timing = 10
# if request.form.get("submit"):
# timing = request.form['timing']
# print(timing)
def countdown(t):
while t:
mins, secs = divmod(t, 60)
timer = '{:02d}:{:02d}'.format(mins, secs)
print(timer, end="\r")
yield timer
time.sleep(1)
t -= 1
# return timer
return app.response_class(countdown(timing)) #at the moment the time value is hardcoded in the function just for simplicity
# return render_template('display.html')
#app.route('/')
def index():
value = "Bonjour"
title_html = value
return render_template('display.html', message=title_html) # render a template at the index. The content will be embedded in this template
if __name__ == '__main__':
app.run(use_reloader=False)
I would like to find the equivalence of print(timer, end="\r") for yield in order to overwrite the value of timer and not see all the results when it's decreasing. I hope my explanation is clear.
Second issue: Input value of the timer
As you can see in my code screen.py, my value for timing is hardcoded timing=10. But I would like to allow the user to enter the value he wants in input like that:
if request.form.get("submit"):
timing = request.form['timing']
print(timing)
You can see these lines in screen.py, I commented them to leave timing=10 because when I write these lines I obtain the following error:
Method Not Allowed
The method is not allowed for the requested URL.
127.0.0.1 - - [02/Aug/2021 12:50:26] "POST / HTTP/1.1" 405 -
Here is the HTML Code linked to my python code display.html:
<!DOCTYPE HTML>
<html>
<head>
<link rel="stylesheet" href='/static/main.css'/>
<title>your dish</title>
</head>
<body>
<h1>{{message}}! Here are some informations about your dish:</h1>
<h2> countdown </h2>
<!-- <p>{{message}}</p> -->
<form method="POST" action=".">
<p><input name="timing" value="{{timing}}" placeholder="Enter your time"></p>
<input type="submit" name="submit" value="submit">
</form>
<div>
<iframe frameborder="0" noresize="noresize"
style='background: transparent; width: 100%; height:100%;' src="{{ url_for('content')}}"></iframe>
</div>
</body>
</html>
How can I avoid this error and take into consideration the value entered by the user in the input field of my display.html?

I tryed to run your script locally but I am not sure where do you expect to see the timer; I assume you used the countdown func from here.
I would like to propose you a different approach: stream dynamically the counter to the web page using an iframe:
from flask import Flask, render_template, Response
import time
app = Flask(__name__)
#app.route('/content') # render the content a url differnt from index. This will be streamed into the iframe
def content():
def timer(t):
for i in range(t):
time.sleep(5) #put 60 here if you want to have seconds
yield str(i)
return Response(timer(10), mimetype='text/html') #at the moment the time value is hardcoded in the function just for simplicity
#app.route('/')
def index():
return render_template('test.html.jinja') # render a template at the index. The content will be embedded in this template
if __name__ == '__main__':
app.run(use_reloader=False)
then add an iframe where do you prefer in your html
<!doctype html>
<head>
<title>Title</title>
</head>
<body>
<h2> countdown </h2>
<div>
<iframe frameborder="0" noresize="noresize"
style='background: transparent; width: 100%; height:100%;' src="{{ url_for('content')}}"></iframe>
</div>
</body>
The result will be a dynamic countdown on your web-page
countdown
0123456789
you can see it done quick and dirty here on my repl
While it's not tuned around your application yet, (and not particularly beautiful graphically) you can modify the function to accept an input from the user with a form (I see you actually did already in your app), or also tune the countdown function directly.
t = request.form['t']
and adding to your html the form
<form method="post" action=".">
<p><input name="t" placeholder="your time"/></p>
<p><input type="submit" value="Submit"/></p>
</form>

You have the same route #app.route("/") appearing 3 times. The system will pick the first one which simply displays display.html. And I suspect even that will currently not work because your page is expecting values for message, timing but those attributes don't exist in your first route.
You should try something like
#app.route("/", methods=['POST', 'GET'])
def display():
page = 'display.html'
params = {"message":"", "timing":0} # initialize values for both message and timing. These will be returned when user loads the page (a GET call)
if request.method == 'POST':
timing = request.values.get("timing")
# do whatever processing you want
params["timing"] = <your computed value>
params["message"] = <your message>
params["message_2"] = <your other message>
return render_template(page, **params)
Delete all the other routes you have for #app.route("/")

Related

how to access form data of a POST request with flask

I hope you can help me.
The fact is that I want to implement the transfer of values ​​from the sliders to the image processing function.
Interface picture
html code:
<form name="uploader" enctype="multipart/form-data" method="POST" action="test">
<div class="slidecontainer">
value1
<input type="range" min="1" max="100" value="50" class="slider" name="slider1">
</div>
</form>
<img src="{{ url_for('output_photo') }}" width="300" height="300" alt="" class="img-thumbnail">
I also have an image processing function that runs when the page loads.
#app.route('/output_photo')
def output_photo():
before_img = cv2.imread('images/1.jpg')
result = before_img.copy()
imgray = cv2.cvtColor(before_img, cv2.COLOR_BGR2GRAY)
filtered = cv2.bilateralFilter(imgray, 11, 50, 100)
output_img = filtered
retval, buffer = cv2.imencode('.png', output_img)
response = make_response(buffer.tobytes())
response.headers['Content-Type'] = 'image/png'
return response
What I need
For example in the filtering method(cv2.bilateralFilter) we have parameters that will need to be changed by the sliders (in this case, it is 11,50,100), but at the same time, this method must have default values ​​so that the image is still processed when the page loads.
How can I that?I will be grateful for the answer
You have to declare the method with the route and then you can access the form data through request.form.get('#ID_NAME')
You'll need both flask and request from flask when importing too.
So, something like this..
#app.route('/edit_img', methods=["POST"])
def edit_img():
img_id = request.form.get('id')
### YOUR CODE
return render_template(# the page and data your rendering)

FastAPI/Starlette, Web UI: Save File Dialog, Open File Dialog

I've been studying this example project. Very concise and to the point, and a pleasant surprise, too: a Save File Dialog (a standard desktop File Dialog, opened from Chrome).
The responsible code:
src/html.py:
#app.post('/download')
def form_post(request: Request, num: int = Form(...), multiply_by_2: bool = Form(False), action: str = Form(...)):
if action == 'convert':
result = spell_number(num, multiply_by_2)
return templates.TemplateResponse('download.html', context={'request': request, 'result': result, 'num': num})
elif action == 'download':
# Requires aiofiles
result = spell_number(num, multiply_by_2)
filepath = save_to_text(result, num)
return FileResponse(filepath, media_type='application/octet-stream', filename='{}.txt'.format(num))
templates/download.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Sample Form</title>
</head>
<body>
<form method="post">
<input type="number" name="num" value="{{ num }}"/>
<input type="checkbox" id="multiply_by_2" name="multiply_by_2" value="True">
<label for="multiply_by_2">Multiply by 2 </label>
<input type="submit" name="action" value="convert">
<input type="submit" name="action" value="download">
</form>
<p>Result: {{ result }}</p>
</body>
</html>
I can't see any hint on a File Dialog in FileResponse, much less on the Save File Dialog, which pops up. I want the Open File Dialog too, by the way. I tried to research it, without success.
How does it work?
UPD, to make myself clearer.
I'm playing with something like this:
from tkinter import Tk
from tkinter.filedialog import askopenfilename
...
#app.post("/open")
def form_post(
request: Request,
action: str = Form("open"),
):
if action == "open":
root = Tk()
root.withdraw()
# ensure the file dialog pops to the top window
root.wm_attributes('-topmost', 1)
fname = askopenfilename(parent=root)
print(f"Chosen file: {fname}")
return templates.TemplateResponse("open.html", context={"request": request})
elif action == "save":
# Requires aiofiles
return FileResponse(
"templates/legacy/download.html",
media_type="text/html",
filename="download.html",
)
For now, button save makes use of the system Save File Dialog, while button open employs tkinter's Open Dialog. It will do, because the whole thing is merely an application with a Web UI. Still, it looks and feels a bit ridiculous.
Is there a way to make the browser serve the Open File Dialg?
The file dialog is what Chrome shows for that kind of response (application/octet-stream). The server side framework isn't responsible for creating the save file dialog; it just provides a response and the browser does what it usually does for the that kind of response.
You can use the Content-Disposition header in your response to indicate to the browser that it should show a download dialog instead of the content directly (if it supports the mime type natively). The Content-Disposition header also allows you to provide a default file name for the save dialog (but only a file name - not a path).
A open file dialog would be the responsibility of the HTML, by using <input type="file" name="name_matching_fastapi_parameter">. You can then tell FastAPI that you expect that a parameter is a file with the UploadFile type.
From the FastAPI reference:
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
#app.post("/files/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
#app.post("/uploadfile/")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}

How do I get the data from a html script tag into my python function?

I am trying to get the data that I have stored inside of a script tag in my html file to my python file so that I can use it in a python function, does anyone have any idea how I could achieve this? Here's my html code:
<textarea id="rendezook" class="rendezook"></textarea>
<button onclick="get_text();">Valider</button>
<script>
function get_text() {
var x = document.getElementById("rendezook")
location.href = "/validate_comment?rendezook=" + encodeURIComponent(x.value);
console.log(x.value)
}
</script>
And then this is my python file:
#app.route('/validate_comment')
def validate_comment(item_id=None):
print("test")
print(request.args.get("rendezook"))
print("test2")
return redirect(url_for('msr_edit', item_id=72))
test and test2 are not getting printed so it is not entering the function.
The easiest way is to just use a form that's submitted via a GET request:
<form action="/validate_comment">
<textarea name="rendezook" class="rendezook"></textarea>
<button type="submit">Valider</button>
</form>
This will result in an URL like /validate_comment?rendezook=some_value_here, so you can access it via request.args (as it looks like you're using Flask):
#app.route('/validate_comment')
def validate_comment():
print("yeet", request.args.get("rendezook"))
return redirect(url_for('msr_edit', item_id=72))
Based on comments, if you're unable to use a form for some reason, then:
<textarea id="rendezook" class="rendezook"></textarea>
<button onclick="test()">Valider</button>
<script>
function test() {
var x = document.getElementById("rendezook");
location.href = "/validate_comment?rendezook=" + encodeURIComponent(x.value);
}
</script>
EDIT 2
Here's a minimal example that provably works :)
from flask import request, Flask
app = Flask(__name__)
#app.route("/validate_comment")
def validate_comment(item_id=None):
x = request.args.get("rendezook")
return f"you said {x}"
#app.route("/")
def index():
return """
<textarea id="rendezook" class="rendezook"></textarea>
<button onclick="get_text();">Valider</button>
<script>
function get_text() {
var x = document.getElementById("rendezook")
location.href = "/validate_comment?rendezook=" + encodeURIComponent(x.value);
console.log(x.value)
}
</script>
"""
if __name__ == "__main__":
app.run()

Methods on linking a HTML Tornado server and Python file

This is my sample HTML file
<html>
<head>
<title>
</title>
</head>
<body>
<form action="">
Value a:<br>
<input type="text" name="Va">
<br>
Value b:<br>
<input type="text" name="Vb">
<br><br>
<input type="submit">
</form>
<textarea rows="4" cols="10">
</textarea>
<p>
</p>
</body>
</html>
And a given template Tornado server code:(I also need help on the explanation of each section of the following code)
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.gen
import tornado.options
tornado.options.parse_command_line()
class APIHandler(tornado.web.RequestHandler):
#tornado.web.asynchronous
def get(self):
self.render('template.html')
#tornado.gen.engine
def post(self):
try:
num = int(self.get_argument('num'))
except:
num = 5
self.render('template.html')
app = tornado.web.Application([(r"/next_rec",APIHandler),])
if __name__ == "__main__":
server = tornado.httpserver.HTTPServer(app)
server.bind(48763)
server.start(5)
tornado.ioloop.IOLoop.current().start()
and finally my python code:
if __name__ == '__main__':
a = int(raw_input())
b = int(raw_input())
print a+b
I am using a simple 'a+b' function to test out this feature. But my problem is I can't figure out a way to link them together. So my ultimate goal is to click on the "Submit" button on the HTML, pass on two values to the Tornado server, use it as input in my python script and finally show the output in the text area of the HTML or on another page. I'm know there are tons of information on the web, but I'm completely new to Tornado (near 0 knowledge) and most of them I can't really understand. Help on methods or keywords for search is much appreciated, thank you very much. (please keep answers as basic as possible, it will help a lot, thanks!)
First of all you should check the official documentation. It is quite simple and it targets the newcomers.
Also in this short guide, the sections of a similar code as your is being explained with simplicity.
Now for your code:
On your template you need to specify that the form should send a post request on submit by adding <form method="post" id="sum_form">
Also you need to make sure that you will be submit the data added in the form on an event: $("#sum_form").submit();
On your post method you need to read the passed numbers from your client's form, add them and then send them back to the template as a parameter.
For example:
def post(self):
numA = int(self.get_argument('Va'))
numB = int(self.get_argument('VB'))
sumAB = numA + numB
self.render('template.html',sumAB=sumAB)
In you template.html you need to add a field where you will display the passed sum as a jinja variable : {{sumAB}}

SL4A _R6 / Python4android_R4 webViewShow NullPointerException on simple example code

I'm trying to get a HTML Gui on my simple SL4A Python app. the only thing I need to perform is ask the user for 1 input string at the start of my app.
For debugging purposes I started of simple. Unfortunately even this won't function.
Any idea's what's wrong here? (I've tried several pieces of code like this and all have the same issue)
Python code:
import android
loop = True
droid = android.Android()
droid.webViewShow('/sdcard/sl4a/scripts/screen.html')
while loop:
res = droid.waitForEvent('data')
if res:
print str(res)
HTML code
<html>
<head>
<script>
var droid = new Android();
var fiets = function() {
droid.postEvent("data", document.getElementById("gsm").value);
}
</script>
</head>
<body onload:"fiets()">
<form onsubmit="fiets(); return false;">
<label for="say">What would you like to say?</label>
<input type="text" id="gsm" value="99999999" />
<input type="submit" value="Store" />
</form>
</body>
</html>
the error message is repeatedly (the number 3114 is incremental):
java.lang.NullPointerException
Result(id=3114, result=None, error=u'java.lang.NullPointerException')
update:
http://www.mithril.com.au/android/doc/EventFacade.html
As we go I read waitForEvent is deprecated in r4. I should use eventWaitFor instead.
This takes the error away but doesn't make the example function. Python script prints nothing.
update2:
droid.postEvent should become droid.eventPost
droid.waitForEvent('data') should become droid.waitForEvent('data')
In javascript and python.
Problem Solved

Categories

Resources