0

Since I couldn't find a suitable solution or useful ideas in previous threads, I'll try asking directly instead.

I am working on a script that downloads and processes geodata in python 2.7. my main code works fine, but since different employer(s) should be able to use my tool, I need a proper interface. I don't want to use pyQt because afaik it must be installed separately and cannot be used by simply importing the .py-file.

So I started out using tkinter.

Right now, my tool can be controlled by a simple gui, that allows the user to enter or alter three text-variables, click start and quit buttons, and read a status bar with (for now) static text.

So if the user hits the start button the main process (where all the downloading and converting work is done) starts.

What I want to do now, is modifying the static status bar with progress updates like "downloading file 10 of 20" or "converting file 11 to feature class / shapefile"

I am not too familiar with tkinter or any kind of gui's yet, so I'll need some advice on how to structure my code with regard to tkinter.

import os
import urllib
import arcpy
from Tkinter import *

def start():
    mainpath= e0.get()
    if not os.path.exists(mainpath+"\\subfolder\\a1"):
        os.makedirs(mainpath+"\\subfolder\\a1")
        os.makedirs(mainpath+"\\subfolder\\a2")
        os.makedirs(mainpath+"\\subfolder\\a3")
        os.makedirs(mainpath+"\\subfolder\\a4")
    else:
        print "folder a1 already exists"

    DOWNLOADS_DIR = mainpath + "\\subfolder\\a3"
    targetsite= e1.get()
    searchstring= e2.get()   

######################################

#now here the downloading and processing is done
#for example:

if not os.path.isfile(filename):
    print "starting download "+element
    urllib.urlretrieve(element, filename)

    # I want to return a status like below to the status bar in the GUI
    status_string = 'downloading file ' + str(i)+ ' from' + str(count) 
    status.set(status_string)
######################################


master = Tk()
Label(master, text="working directory: ").grid(row=0)
Label(master, text="URL: ").grid(row=1)
Label(master, text="searchstring: ").grid(row=2)

mainpathstring = StringVar(master, value='D:\project')
e0 = Entry(master, textvariable=mainpathstring, width="50")

linkstring = StringVar(master, value='http://thedefaultwebsite.com')
e1 = Entry(master, textvariable=linkstring, width="50")

searchstring = StringVar(master, value='coordinates')
e2 = Entry(master, textvariable=searchstring, width="50")

e0.grid(row=0, column=1)
e1.grid(row=1, column=1)
e2.grid(row=2, column=1)

Button(master, text='quit', command=master.quit).grid(row=3, column=0, sticky=W, pady=4)
Button(master, text='start', command=start).grid(row=3, column=1, sticky=W, pady=4)

status= Label(master, text="working on: nothing", bd=1, relief=SUNKEN, anchor=W).grid(row=4, column=0, columnspan=3)

mainloop( )

How should this be approached? Where can I find good examples to toy with?

Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52
fry82
  • 1
  • `ttk` has a progress bar widget look [here](http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/ttk-Progressbar.html) for info – Steven Summers Dec 28 '15 at 14:15
  • I already experimented with this module, but I think it is designed for PROGRESS bars...I couldn't figure out a way to precalculate the time my script will take @Steven Summers – fry82 Dec 28 '15 at 14:23
  • Are you able to determine the total number of files to be downloaded? If not then I doubt you can do _downloading file 10 of 20_. Do you not have a list you can perform `len(file_list)` to get the number of items? – Steven Summers Dec 28 '15 at 14:45
  • if you start downloading in main thread then it stops mainloop and widgets will not work. btw: full working example with two types of progressbars http://stackoverflow.com/a/24770800/1832058 – furas Dec 28 '15 at 14:55
  • @Steven Summers: yes, I search the html code and generate a list of valid links. this way I create a list of links which lenght I determine with the len function which you mentioned.later on I loop through that list. I thought I could call a function which updates the status from there...is that possible? depending on the filesize the processing time varies, so that a process bar doesn't make too much sense... – fry82 Dec 28 '15 at 15:21
  • Well I'm assuming you're iterating through the list, couldn't you update the progress bar with each iteration? That way with each completed iteration a file is done. – Steven Summers Dec 28 '15 at 16:30
  • that's exactly what I was trying to do. But I can't figure out how. The whole structure of the tkinter module as well als tkk seems very confusing to me. I read up on several commands and methods and even if the codesnippets work, I don't understand how they work. methods seem to be called with no clear calls, parameters appear without being handed to them etc. – fry82 Dec 28 '15 at 18:27
  • I changed the (misleading) title from 'progress bar' (`[xxxxxx........]`) to 'progress updates` to better match the question in the body. – Terry Jan Reedy Dec 28 '15 at 22:29

1 Answers1

0

There are many good tkinter examples in answers to SO questions. A few by me, many others by Bryan Oakley and others.

Tkinter code can be structured either as imperative code, with toplevel functions definitions and other statements, as you did, or as OO code, with class definitions and minimal toplevel code otherwise. You will see examples of both.

The specific answer to status updates is a Label with a StringVar textvariable. Updating the textvariable updates the display. The following illustrates the idea. (This is Python 3 code, change 'tkinter' to 'Tkinter' for Python 2.)

import time
import tkinter as tk
root = tk.Tk()
current = tk.StringVar(root)  # initial value ''
status = tk.Label(root, textvariable=current)
status.pack()
for i in range(1, 4):
    current.set('downloading file %d' % i)
    root.update()
    time.sleep(1)
current.set('downloading done')

The for loop, .update, and especially the .sleep are for this example only.

You did not specifiy whether or not you cared about keeping the GUI responsive during the download process. The minimal and easy thing to do would be to download one file at a time, using root.after. The GUI would then process accumulated events after each download. There are many good examples of using .after in SO answers.

More advanced is downloading files in a separate thread. On *nix, tk has a function for this that makes it pretty easy. For Windows, I would would look for a pre-written function elsewhere.

Terry Jan Reedy
  • 18,414
  • 3
  • 40
  • 52