From 864622bdf4e7889acaf4115523197eb7cba38650 Mon Sep 17 00:00:00 2001 From: Tyrel Souza Date: Mon, 20 Jul 2015 12:23:44 -0400 Subject: [PATCH] Progress bar, multithreading --- main.py | 75 ++++++++++++++++++++++++++++++++++++------------ requirements.txt | 1 + setup.py | 15 ++-------- utils.py | 18 +++++++++++- 4 files changed, 77 insertions(+), 32 deletions(-) diff --git a/main.py b/main.py index 2da4316..060dd04 100644 --- a/main.py +++ b/main.py @@ -2,9 +2,11 @@ from attribution import AttributionReport __author__ = 'tsouza' import Tkinter as tk +from ttk import Progressbar import tkFileDialog import tkMessageBox -from utils import get_dropbox_dir +from utils import get_dropbox_dir, ThreadedTask +import Queue class AttributeGUI(tk.Tk): @@ -19,11 +21,15 @@ class AttributeGUI(tk.Tk): 'dp_label': u"", 'output_label': u"" } + self.initGUI() self.reset() def reset(self): - self.initGUI() self.report = AttributionReport(months=6, footer_length=6) # TODO MAKE MONTHS A DROPDOWN + self.input_frame.sf_label_var.set(self.defaults['sf_label']) + self.input_frame.dp_label_var.set(self.defaults['dp_label']) + self.output_frame.output_label_var.set(self.defaults['output_label']) + self.run_frame.run_button.config(text="Run") self._check_ready_to_run() def initGUI(self): @@ -46,16 +52,24 @@ class AttributeGUI(tk.Tk): self.run_frame.grid(row=2, column=0) self.rowconfigure(2, weight=0) + # Progress bar + self.progress_bar = Progressbar(self, mode="indeterminate") + self.progress_bar.grid(column=0, row=3) + self.update() self.minsize(self.winfo_width(), self.winfo_height()) self.resizable(True, False) + # ___ ___ _ __ __ ___ ___ + # | __| _ \ /_\ | \/ | __/ __| + # | _|| / / _ \| |\/| | _|\__ \ + # |_| |_|_\/_/ \_\_| |_|___|___/ def setup_input_frame(self): input_frame = tk.LabelFrame(text="Input Files") input_frame.grid() row = 0 - input_frame.sf_button = tk.Button(input_frame, text=u"Browse", command=self.sf_click) + input_frame.sf_button = tk.Button(input_frame, text=u"Browse", command=self.callback_sf_browse_click) input_frame.sf_button.grid(column=0, row=row) input_frame.sf_label_var = tk.StringVar() input_frame.sf_label = tk.Label(input_frame, @@ -66,7 +80,7 @@ class AttributeGUI(tk.Tk): row += 1 - input_frame.dp_button = tk.Button(input_frame, text=u"Browse", command=self.dp_click) + input_frame.dp_button = tk.Button(input_frame, text=u"Browse", command=self.callback_dp_browse_click) input_frame.dp_button.grid(column=0, row=row) input_frame.dp_label_var = tk.StringVar() input_frame.dp_label = tk.Label(input_frame, @@ -82,7 +96,7 @@ class AttributeGUI(tk.Tk): output_frame.grid() row = 0 - output_frame.output_button = tk.Button(output_frame, text=u"Browse", command=self.output_click) + output_frame.output_button = tk.Button(output_frame, text=u"Browse", command=self.callback_output_browse_click) output_frame.output_button.grid(column=0, row=row) output_frame.output_label_var = tk.StringVar() output_frame.sf_label = tk.Label(output_frame, @@ -98,12 +112,17 @@ class AttributeGUI(tk.Tk): run_frame = tk.LabelFrame(text="Output Directory") run_frame.grid() row = 0 - run_frame.run_button = tk.Button(run_frame, text=u"Run", command=self.run_click) + run_frame.run_button = tk.Button(run_frame, text=u"Run", command=self.callback_run_click) run_frame.run_button.grid(column=0, row=row) + run_frame.grid_columnconfigure(0, weight=1) return run_frame - def sf_click(self): + # ___ _ _ _ ___ _ ___ _ _____ + # / __| /_\ | | | | | _ ) /_\ / __| |/ / __| + # | (__ / _ \| |__| |__| _ \/ _ \ (__| ' <\__ \ + # \___/_/ \_\____|____|___/_/ \_\___|_|\_\___/ + def callback_sf_browse_click(self): self.salesforce_filename = tkFileDialog.askopenfilename( title="Salesforce Export File", initialdir=get_dropbox_dir(), @@ -128,7 +147,7 @@ class AttributeGUI(tk.Tk): ) self._check_ready_to_run() - def dp_click(self): + def callback_dp_browse_click(self): self.deposit_filename = tkFileDialog.askopenfilename( title="Deposit Data Export File", initialdir=get_dropbox_dir(), @@ -152,7 +171,7 @@ class AttributeGUI(tk.Tk): ",\n".join(self.report.REQUIRED_DP_COLUMNS))) self._check_ready_to_run() - def output_click(self): + def callback_output_browse_click(self): self.output_directoryname = tkFileDialog.askdirectory( initialdir=get_dropbox_dir(), ) @@ -161,23 +180,41 @@ class AttributeGUI(tk.Tk): self.output_frame.output_label_var.set(self.output_directoryname) self._check_ready_to_run() + def callback_run_click(self): + """ + When you click run, the progress bar and button notify you that something is happening. + This then starts a thread that sets the report Running and report Saving. + """ + self.progress_bar.start() + self.run_frame.run_button.config(text="Running...") + self.queue = Queue.Queue() + ThreadedTask(self.queue, self.report).start() + self.after(100, self._process_queue) + def _check_ready_to_run(self): - if self.report.salesforce_df is not None and self.report.deposit_df is not None and self.report.output_dir: + """ + Disables and re-enables the Run button when criteria is met. + """ + if self.report.salesforce_df is not None \ + and self.report.deposit_df is not None \ + and self.report.output_dir: self.run_frame.run_button.config(state=tk.NORMAL) else: self.run_frame.run_button.config(state=tk.DISABLED) self.update() - - def run_click(self): + def _process_queue(self): + """ + Multi-threading the running stuff so that the UI doesn't lock. + """ try: - self.report.run() - self.report.save() - except Exception, e: - tkMessageBox.showerror("EXCEPTION", str(e)) - self.quit() - return - self.reset() + self.queue.get(0) + # Show result of the task if needed + self.progress_bar.stop() + self.reset() + except Queue.Empty: + self.after(100, self._process_queue) + def main(): app = AttributeGUI() diff --git a/requirements.txt b/requirements.txt index dd5fedd..6b0ed95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ XlsxWriter==0.7.3 pandas==0.16.2 python-dateutil==2.4.2 xlrd==0.9.4 +pyttk==0.3.2 diff --git a/setup.py b/setup.py index 5683e7a..71bec8f 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,7 @@ # -*- coding: utf-8 -*- - -from distutils.core import setup -from glob import glob -import py2exe +from setuptools import setup setup( - data_files = [("Microsoft.VC90.CRT", glob(r'C:\Program Files\Microsoft Visual Studio 9.0\VC\redist\x86\Microsoft.VC90.CRT\*.*'))], - options={ - "py2exe":{ - - "dll_excludes": ["MSVCP90.dll", ] - } - }, - console=["attribution.py"] + app=["main.py"], + setup_requires=["py2app"], ) diff --git a/utils.py b/utils.py index 0ff60d4..64be5c3 100644 --- a/utils.py +++ b/utils.py @@ -1,6 +1,9 @@ __author__ = 'tyrelsouza' import os import json +import threading +import time + def get_dropbox_dir(): """ @@ -21,4 +24,17 @@ def get_dropbox_dir(): else: dropbox_dir = os.path.expanduser("~") - return dropbox_dir \ No newline at end of file + return dropbox_dir + + + +class ThreadedTask(threading.Thread): + def __init__(self, queue, report): + threading.Thread.__init__(self) + self.queue = queue + self.report = report + + def run(self): + self.report.run() + self.report.save() + self.queue.put("Task finished") \ No newline at end of file