0

I'm trying to build a flask application that gives me the possibility to visualize a text file.

I'm able to open the text file and use it in my html file!

@app.route('/texts')
def texts():
with open("textfile.txt", "r") as f: 
    content = f.read() 
return render_template("texts.html", content=content)

In HTML :

<div class="textzone">
  <pre>{{ content }}</pre> 
</div>

In CSS:

.textzone {
overflow: scroll;
border: 3px solid #009688;
width: 330px;
background-color: rgb(231, 226, 226);
height:330px;
position: relative;
left: 125px;
top:80px;
-webkit-box-shadow: 11px 11px 10px 2px #262626; 
box-shadow: 11px 11px 10px 2px #262626;
padding: 20px;
}

When I update the text file I can see the updates on the front end only after refreshing the page!

I want to ask if there's any solution to see the changes in the front end without having to refresh the page ?

I would like to see the changes I've applied to the text file without having to refresh the page!

Thanks!

Anis Zidi
  • 41
  • 5
  • See this: https://stackoverflow.com/questions/16344756/auto-reloading-python-flask-app-upon-code-changes. – CypherX Apr 17 '21 at 01:25
  • @CypherX From my understanding he is speaking about the `textfile.txt`, not Python script. – felipe Apr 17 '21 at 01:26
  • @Felipe I shared [another link](https://stackoverflow.com/a/16373705/8474894) for the OP to use a `textfile.txt` file. – CypherX Apr 17 '21 at 01:33
  • @Anis When the client sends a request to Flask the server respond with some HTML render (template + user context) and closes the connection. To update the client's side from the server end you need some extra overhead. You should look into either Websockets (open data stream between client/server), or timed requests from the client side (via JS in the HTML) to query the endpoint continuously at an interval for updates. – felipe Apr 17 '21 at 01:36

2 Answers2

1

There are ways to bootleg this with Flask, but it's not recommended. In short, Flask does not have good support for multithreading, and parallel execution is one of the requirements needed for your solution (assuming you are planning on doing this the "more" standard way).

As I mentioned in the comments Flask alone cannot do what you are trying to do. The reason for this is because in a typical HTTP/1.1 transaction (the protocol your browser and Flask are using to communicate) does not have support for bidirectional data streams. In other words: when a client sends a request to the server Flask will response with the rendered HTML page and then close the connection. What you need is for the server to preserve the connection with the client, and send to the client any updates to the textfile.txt.

To achieve this you need to use another protocol both on the client and the server end, as well as asynchronous code execution on the server end.

Regarding protocols, the standard is Websocket for these sort of things. Regarding asynchronous code execution, this is a bit of an issue with Flask: one of the requirements of this problem is the need for the server to (1) keep track of the text file, and (2) send text file to client on update. To do this with Flask would require some multithreading, which is not very Flask-friendly. Because of this I would actually recommend looking into something like FastAPI, which comes with Websocket built-in out of the box, and can be ran asynchronously.

I should also mention that the problem you are facing is rather quite unique, and something I've personally never seen being applicable. I can't confirm it, but this does seem like a XY problem.


As an example, here is some code to do what you are looking for using Flask, Flask-SocketIO (pip install flask-socketio), and multithreading.

Note that Socket.IO is built on top of websocket and requires its own supporting code for both the client and server end. I won't go into the details on why one over the other, but just understand that there is a significant difference between vanilla Websocket and Socket.IO in terms of usage, application, etc. Also note that using threading with Flask causes undefined behaviors that are annoying to have to deal with. Things like auto-reloading will break and force you to constantly reload the app.

Again: I can't express enough how I feel this is a problem better solved with FastAPI (due to the ability of asynchronous code execution), but for the sake of answering your question directly here is a subpar implementation with Flask:

from flask import Flask, render_template
from flask_socketio import SocketIO
from threading import Thread
from time import sleep

app = Flask(__name__)
io = SocketIO(app)


@app.route("/")
def index():
    return render_template("texts.html", content=get_file())


def get_file(loop=False, io=None):
    text = open("textfile.txt", "rb")

    def read():
        data = text.read()
        text.seek(0)
        return data

    old = read()
    while loop:

        current = read()
        if current != old:
            io.emit("text", read().decode("utf-8"), broadcast=True)
            old = current

        sleep(1)

    return text.read().decode("utf-8")


if __name__ == "__main__":
    Thread(target=lambda: get_file(loop=True, io=io)).start()
    io.run(app, debug=True)

In the client side you need to have:

<html>

<head>
    <style>
        .textzone {
            overflow: scroll;
            border: 3px solid #009688;
            width: 330px;
            background-color: rgb(231, 226, 226);
            height: 330px;
            position: relative;
            left: 125px;
            top: 80px;
            -webkit-box-shadow: 11px 11px 10px 2px #262626;
            box-shadow: 11px 11px 10px 2px #262626;
            padding: 20px;
        }
    </style>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <script type="text/javascript" charset="utf-8">
        console.log("running.");
        const socket = io('http://127.0.0.1:5000')

        socket.on('text', function(msg) {
            document.getElementById("content").innerHTML = msg;
        });
    </script>
</head>

<body>
    <div class="textzone">
        <pre id="content">{{ content }}</pre> 
    </div>
</body>

</html>
felipe
  • 7,324
  • 2
  • 28
  • 37
1

Hello everyone thanks for your help! I found a solution for my problem !

I used AJAX ! Well I will explain my solution :

I added a route within my flask application to call it every time I want to update :

@app.route('/update_text', methods=['POST'])
def updatetext():
with open("textfile.txt", "r") as f: 
    content = f.read()
return jsonify('', render_template('updated_text_module.html', x=content))

Then I used AJAX in my html file to define functions :

<script   src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>

<script>

$(function(){
    window.setInterval(function(){
     loadNewText()
  },9999)


function loadNewText(){
  $.ajax({
    url:"/update_text",
    type: "POST",
    dataType: "json",
    success: function(data){
        $(new_text).replaceWith(data)
    }

   });
  }
 });

 </script>

I used another template in which I've stored the updated text :

<div id="new_text" class="textzone">
 {{ x }}
</div>

This was the solution for my problem ! Thanks everyone for your help If anyone needs further explanations I will answer your comments !

Anis Zidi
  • 41
  • 5
  • If it works for you, case closed - glad you found a solution. Make sure to press the check mark next to your post to mark this question as answered! – felipe Apr 18 '21 at 17:04