777winters
777winters

Reputation: 31

Progress bar in Python using rich

I am making a File Sharing Program using sockets in python. I wanna show the transfer progress by making use of progress bar in rich. But the progress bar is not properly synced with the transfer progress

sender script-

import socket, os, time
from rich.console import Console
from rich.progress import Progress


HOST = socket.gethostbyname(socket.gethostname())
PORT = 12345
ADDR = (HOST, PORT)
BUFSIZ = 4096
FORMAT = "utf-8"
SEPARATOR = "<SEPARATOR>"


console = Console()


FILENAMES = ["file.txt", "lol.txt"]
FILSIZ = [str(os.path.getsize(x)) for x in FILENAMES]


def send():
    """main function to send files"""
    console.clear()

    # creating a client socket
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client.connect(ADDR)
    print(client.recv(BUFSIZ).decode(FORMAT))

    # sending file data
    client.send(SEPARATOR.join(FILENAMES).encode(FORMAT))
    print(client.recv(BUFSIZ).decode(FORMAT))
    client.send(SEPARATOR.join(FILSIZ).encode(FORMAT))
    print(client.recv(BUFSIZ).decode(FORMAT))

    # sending files
    for idx, files in enumerate(FILENAMES):
        with open(files, "rb") as f, Progress() as progress:
            task = progress.add_task(f"Sending {files}", total=int(FILSIZ[idx]))

            client.send(f.read(int(FILSIZ[idx])))

            while not progress.finished:
                progress.update(task, advance="<AMOUNT_OF_DATA_OR_CHUNKS_SENT>")
                time.sleep(0.1)

        f.close()


    # closing connection
    client.close()

send()

receiver script - https://www.toptal.com/developers/hastebin/avomadisox.py

afaik advance value must be amount of data or chunks sent(might be wrong here)... how do i calculate the amount of data sent?

Upvotes: 2

Views: 7064

Answers (2)

hc_dev
hc_dev

Reputation: 9377

Your main question asks for

  • How to calculate the amount of data sent?

From Real Python's Socket Programming in Python (Guide):

The .send() method also behaves this way. It returns the number of bytes sent, which may be less than the size of the data passed in.

This means that you have to pass the returned int of socket.send() to the parameter advance of progress.update() function (compare your "<AMOUNT_OF_DATA_OR_CHUNKS_SENT>"):

# (1) define the transfer function returning the bytes_sent
def transfer_to_socket(from_file, to_socket, size):
    bytes_to_read = int(size)
    chunk = from_file.read(bytes_to_read)
    
    # <AMOUNT_OF_DATA_OR_CHUNKS_SENT>
    return to_socket.send(chunk)  # may be: bytes_sent < len(chunk) < bytes_to_read 

# (2) replace the progress-loop with: 
while not progress.finished:
    bytes_sent = transfer_to_socket(f, client, FILSIZ[idx])
    progress.update(task, advance=bytes_sent)
    time.sleep(0.1)

Upvotes: 2

cbare
cbare

Reputation: 12468

Rich's progress bars are nice!

For many use-cases, the track function that wraps a Sequence or Iterable will suffice:

import time
from rich.progress import track

for i in track(range(100)):
    time.sleep(0.05)

To increment progress by a variable amount at each step, use rich.progress.Progress.

This example might show something in the spirit of the original question. Just for fun, let's customize the progress bar while we're at it.

import time
import random
from rich.progress import (
    BarColumn,
    Progress,
    SpinnerColumn,
    TaskProgressColumn,
    TimeElapsedColumn,
    TimeRemainingColumn,
)

def process(chunks):
    for chunk in chunks:
        time.sleep(0.1)
        yield chunk

chunks = [random.randint(1,20) for _ in range(100)]

progress_columns = (
    SpinnerColumn(),
    "[progress.description]{task.description}",
    BarColumn(),
    TaskProgressColumn(),
    "Elapsed:",
    TimeElapsedColumn(),
    "Remaining:",
    TimeRemainingColumn(),
)

with Progress(*progress_columns) as progress_bar:
    task = progress_bar.add_task("[blue]Downloading...", total=sum(chunks))
    for chunk in process(chunks):
        progress_bar.update(task, advance=chunk)

Note: The generator process(chunks) is a generic stand-in for the file sizes in original question. This answer is mostly for the benefit of those brought here by searching on something like "python rich.progress_bar.ProgressBar example".

Upvotes: 4

Related Questions