Annonymous
Annonymous

Reputation: 978

How to get tk to display something before a function is finished

I have this code which downloads the files specified in an online list and whilst doing that, display a loading screen with a label telling of which file is being downloaded and an indeterminate progress bar showing to the user something is happening. The download works very well, but what doesn't work is tkinter, which doesn't work until the process is finished, only showing the "Download Finished" label at the end. How can I get tkinter to display a window before a function is finished?

I have already tried

To display this what I am talking about, I have replaced my original code with some examples. Does anyone know how to make tkinter update more actively (before the function has finished)?

#Imports
import urllib.request as ur
import os
from tkinter import *
import tkinter.ttk as ttk
import time as T

#Globals
tk = None
where = None
progressbar = None
progresstitle = None
progressinfo = None
transfer = None

#Make sure that the download directory exists
def checkdir(filename):
    directory = os.path.dirname(filename)
    try:
        os.stat(directory)
    except:
        os.mkdir(directory)  

#First part (read q to see why I split up)
def part1():
    #Get Globals
    global tk
    global where
    global progressbar
    global progresstitle
    global progressinfo
    global transfer

    #Create Window
    tk = Tk()
    tk.title("Downloading...")

    #Find out the location of the online files to download by reading the online txt file which contains their locations
    where = str(ur.urlopen("http://example.com/whatfilestodownload.txt").read())
    where = where[2:(len(where)-1)]
    where = where.split(";")
    #Create the progress bar
    progressbar = ttk.Progressbar(tk, orient=HORIZONTAL, length=200, mode='indeterminate')
    progressbar.grid(row = 2, column = 0)
    #Create the labels
    progresstitle = Label(tk, text = "Downloading Files!", font = ("Helvetica", 14))
    progresstitle.grid(row = 0, column = 0)
    progressinfo = Label(tk, text = "Starting Download...", font = ("Helvetica", 10))
    progressinfo.grid(row = 1, column = 0)

    #Engage Part Two
    part2()

#Part Two
def part2():
    #Get Globals
    global tk
    global where
    global progressbar
    global progresstitle
    global progressinfo
    global transfer
    #Repeat as many times as files described in the only file describing .txt
    for x in where
        #The online file contains "onlinelocation:offlinelocation" This splits them up
        x1 = x.split(":")[0]
        x2 = x.split(":")[1]

        #Read the online file and update labels
        transfer = None
        progressinfo.config(text = str("Downloading " + x2 + "..."))
        transfer = str(ur.urlopen("http://example.com/" + x1).read())
        progressinfo.config(text = str("Configuring downloaded file..."))
        transfer = transfer [2:(len(transfer)-1)]

        #Fix python turning "\n" to "\\n" by reversing
        transfer = transfer.split("\\n")
        transtemp = ""
        for x in transfer:
            transtemp = transtemp + "\n" + x
        transfer = transtemp[1:len(transtemp)]
        progressinfo.config(text = str("Installing " + x2 + "..."))
        tw = None
        checkdir(str(os.getcwd()+"/Downladed/"+x2))
        tw = open(str(os.getcwd()+"/Downloaded/"+x2), "w")
        tw.write(transfer)
        tw.close()
        #See if waiting helps
        T.sleep(0.5)
    part3()
def part3():
    #Get Globals
    global tk
    global where
    global progressbar
    global progresstitle
    global progressinfo
    global transfer
    #Final Screen
    progressbar.grid_remove()
    progressinfo.grid_remove()
    progresstitle.config(text="You have downloaded\n the required files!")
    progressbar.stop()

part1()

Upvotes: 0

Views: 1152

Answers (2)

atlasologist
atlasologist

Reputation: 3964

If updating the display at the end of each file being downloaded in your part2() function is enough, you can use the update_idletasks() method, putting it in place of T.sleep(), which will allow the GUI to refresh between going back to another iteration of your for loop.

Ref: http://effbot.org/tkinterbook/widget.htm#Tkinter.Widget.update_idletasks-method

Upvotes: 1

patthoyts
patthoyts

Reputation: 33203

This is why Tcl has asynchronous I/O functions. You need to keep processing windowing system events in a timely manner so you cannot wait for a complete file to download. Instead you need to do it in pieces. In Tcl we would use the fileevent command to set a procedure to be called each time some input became available from the socket. The rest of the time we can process other events. In Python the common way to do this is the Twisted package. This allows you to register event sources with twisted and make the entire application event oriented. You could also use threads and do the downloading on worker threads but that doesn't really help you with the progress notifications. There is some special handling to link up Tkinter and Twisted - see the documentation.

Upvotes: 1

Related Questions