#!/usr/bin/env python # -*- coding: utf-8 -*- """ Scoary - Microbial Pan-GWAS GUI By Ola Brynildsrud Norwegian Institute of Public Health olbb@fhi.no """ import sys, os import threading try: import Tkinter except ImportError: #Python 3 issues try: import tkinter as Tkinter except: sys.exit("Need to have Tkinter / tkinter installed") try: import tkFileDialog except ImportError: # Python 3 issues try: from tkinter import filedialog tkFileDialog = filedialog except: sys.exit("Could not find tkFileDialog / filedialog") try: import ttk except ImportError: # Python 3 issues try: from tkinter import ttk as ttk except: sys.exit("Could not find ttk / tkinter.ttk") try: ttk Tkinter except NameError: sys.exit("Need the following installed: Tkinter, tkFileDialog, ttk") try: import os except ImportError: sys.exit("Need to be able to import os") try: import argparse except ImportError: sys.exit("Need to be able to import argparse") try: import scoary.methods as sm import scoary except ImportError: sys.exit("Could not find the main Scoary executable") from pkg_resources import resource_string, resource_filename class ScoaryGUI(Tkinter.Tk): """ Create the main GUI window """ def __init__(self,parent): Tkinter.Tk.__init__(self,parent) self.parent = parent self.Scoary_parameters = {"GPA": None,"Trait":None,"Tree":None, "Restrict":None, "Writetree": False, "Delimiter":",", "Startcol": "15", "Maxhits":None, "Notime":False, "Outdir":None, "Permutations":0, "No_pairwise":False, "Collapse":False, "Cutoffs": {"I": [1,0.05], "B": [0,1.0], "BH": [0,1.0], "PW": [0,1.0], "EPW": [1,0.05], "P":[0,1.0]}, } self.initialize_menu() self.toppart = Tkinter.Frame(self,height="350",width="800") self.bottompart = Tkinter.Frame(self,height="50",width="800") self.toppart.pack(side='top',expand=False) self.bottompart.pack(side='bottom',expand=True,fill='both') self.nwpart = Tkinter.Frame(self.toppart, height="350", width="250") self.nepart = Tkinter.LabelFrame(self.toppart, height="350", width="550", text="Control panel") self.nwpart.pack(side='left',expand=False) self.nepart.pack(side='right',expand=True) # Add further frames to top or bottom self.logocanvas = Tkinter.Canvas(self.nwpart, height="250", width="250", relief="ridge", bd=0, highlightthickness=0) self.logocanvas.pack(side='top',expand=False) self.citationframe = Tkinter.Frame(self.nwpart, height="100", width="250") self.citationframe.pack(side='bottom',expand=False,fill='none') self.initialize_controlboard() self.initialize_logo() self.initialize_citation() self.initialize_statusframe() ######## MENUS ######## def initialize_menu(self): """ Initialize the menu at the top """ self.menubar = Tkinter.Menu(self,relief="flat") filemenu = Tkinter.Menu(self.menubar,tearoff=0) filemenu.add_command(label="About",command=self.AboutScoary) filemenu.add_separator() filemenu.add_command(label="Quit",command=self.quit) self.filemenu = filemenu self.menubar.add_cascade(label="File",menu=self.filemenu) optsmenu = Tkinter.Menu(self.menubar,tearoff=0) optsmenu.add_command(label="Clear", command=self.ClearAll) optsmenu.add_command(label="Test example", command=self.TestExample) self.optsmenu = optsmenu self.menubar.add_cascade(label="Options",menu=self.optsmenu) self.config(menu=self.menubar) def initialize_citation(self): """ Initialize the citation frame - below the logo """ myfontstyle = ("Arial",8) self.citation = Tkinter.Label(self.citationframe, text=self.citationtext(), anchor='center', justify='center', font=myfontstyle) self.citation.pack(expand=True) def initialize_statusframe(self): """ Initialize the frame and statusbar occupying the bottom """ frame = self.bottompart frame.pb = ttk.Progressbar(frame, orient='horizontal', mode='determinate', maximum=100) frame.pb.pack(fill='both',expand=True,side='top') frame.lab = Tkinter.Label(frame,text=u"Awaiting input options") frame.lab.pack(in_=frame.pb,expand=True) #sys.stdout = StdoutToLabel(frame.lab, progressbar=frame.pb) sys.stdout = StdoutToLabel(frame.lab, progressbar=frame.pb, width=frame.cget('width')) def initialize_controlboard(self): """ Initialize the controlboard - where all the settings are set. """ board = self.nepart self.GPAentryVariable = Tkinter.StringVar() board.GPAentry = \ Tkinter.Entry(board, textvariable = self.GPAentryVariable, width=60) board.GPAentry.grid(column=0,row=0,sticky='W',columnspan=2) self.GPAentryVariable.set("Path to gene presence absence file") self.TraitsentryVariable = Tkinter.StringVar() board.Traitsentry = \ Tkinter.Entry(board, textvariable=self.TraitsentryVariable, width=60) board.Traitsentry.grid(column=0,row=1,sticky='W',columnspan=2) self.TraitsentryVariable.set("Path to traits/phenotype file") self.TreeentryVariable = Tkinter.StringVar() board.Treeentry = \ Tkinter.Entry(board, textvariable=self.TreeentryVariable, width=60) board.Treeentry.grid(column=0,row=2,sticky='W',columnspan=2) self.TreeentryVariable.set( "(Optional) Path to custom tree file") self.RestrictVariable = Tkinter.StringVar() board.Restrictentry = \ Tkinter.Entry(board, textvariable=self.RestrictVariable, width=60) board.Restrictentry.grid(column=0,row=3,sticky='W',columnspan=2) self.RestrictVariable.set( "(Optional) Path to file naming isolates to include") self.Outputdir = Tkinter.StringVar() board.Outputentry = \ Tkinter.Entry(board, textvariable=self.Outputdir, width=60) board.Outputentry.grid(column=0,row=4,sticky='W',columnspan=2) self.Outputdir.set("(Optional) Output directory") browsebuttonGPA = \ Tkinter.Button(board, text=u"Browse...", command=self.BrowseButtonClickGPA) browsebuttonGPA.grid(column=2,row=0,sticky='e') browsebuttonTraits = \ Tkinter.Button(board, text=u"Browse...", command=self.BrowseButtonClickTraits) browsebuttonTraits.grid(column=2,row=1,sticky='e') browsebuttonTreeFile = \ Tkinter.Button(board, text=u"Browse...", command=self.BrowseButtonClickTreeFile) browsebuttonTreeFile.grid(column=2,row=2,sticky='e') browsebuttonRestrict = \ Tkinter.Button(board, text=u"Browse...", command=self.BrowseButtonClickRestrict) browsebuttonRestrict.grid(column=2,row=3,sticky='e') browsebuttonOutput = \ Tkinter.Button(board, text=u"Browse...", command=self.BrowseButtonClickOutput) browsebuttonOutput.grid(column=2,row=4,sticky='e') # Initialize frame for cutoffs board.pframe = Tkinter.LabelFrame(board, text="Cut-offs", relief='ridge') board.pframe.grid(column=0,row=5,sticky='w') self.initialize_pvalueframe() # Initialize frame for misc options board.miscframe = Tkinter.LabelFrame(board, text="Misc options", relief='ridge') board.miscframe.grid(column=1,row=5,sticky='e',columnspan=2) self.initialize_miscopts() ## Create extra space board.emptyspace = Tkinter.Frame(board) board.emptyspace.grid(column=0,row=6,columnspan=3,pady=(20,20)) masterbuttonfont = ("Courier", 16) RunButton = Tkinter.Button(board.emptyspace, text=u"Run analysis", font=masterbuttonfont, command=self.RunAnalysis, padx=15,pady=10) RunButton.grid(column=2,row=0) QuitButton = Tkinter.Button(board.emptyspace, text=u"Quit", font=masterbuttonfont, command=self.quit,padx=15,pady=10) QuitButton.grid(column=1,row=0) HelpButton = Tkinter.Button(board.emptyspace, text=u"Help", font=masterbuttonfont, command=self.HelpButton, padx=15,pady=10) HelpButton.grid(column=0,row=0) def initialize_miscopts(self): """ Initialize the miscellaneous options """ board = self.nepart miscframe = board.miscframe # Max hits self.mhtext = Tkinter.StringVar() miscframe.mhlab = Tkinter.Label(miscframe, textvariable=self.mhtext) miscframe.mhlab.grid(column=0,row=0,sticky='w') self.mhtext.set("Max hits") self.maxhitsvar = Tkinter.StringVar() miscframe.maxhitsentry = \ Tkinter.Entry(miscframe, textvariable=self.maxhitsvar, width=8) miscframe.maxhitsentry.grid(column=1,row=0) self.maxhitsvar.set("") # Delimiter self.delimtext = Tkinter.StringVar() miscframe.delim = \ Tkinter.Label(miscframe, textvariable=self.delimtext) miscframe.delim.grid(column=0,row=1,sticky='w') self.delimtext.set("Delimiter") self.delimvar = Tkinter.StringVar() miscframe.delimentry = \ Tkinter.Entry(miscframe, textvariable=self.delimvar, width=8) miscframe.delimentry.grid(column=1,row=1) self.delimvar.set(",") # Starting column self.sctext = Tkinter.StringVar() miscframe.sclab = \ Tkinter.Label(miscframe, textvariable=self.sctext) miscframe.sclab.grid(row=2,column=0,sticky='w') self.sctext.set("Startcol GPA file") self.scvar = Tkinter.StringVar() miscframe.sc = Tkinter.Entry(miscframe,textvariable=self.scvar,width=8) miscframe.sc.grid(row=2,column=1) self.scvar.set("15") # Permutations self.permtext = Tkinter.StringVar() miscframe.permlab = \ Tkinter.Label(miscframe, textvariable=self.permtext) miscframe.permlab.grid(row=3,column=0,sticky='w') self.permtext.set("Permutations") self.permvar = Tkinter.StringVar() miscframe.perm = \ Tkinter.Entry(miscframe, textvariable=self.permvar, width=8) miscframe.perm.grid(row=3,column=1) self.permvar.set("0") # No timestamp self.notimevar = Tkinter.IntVar() miscframe.notime = \ Tkinter.Checkbutton(miscframe, text=u"No timestamp", onvalue=1, offvalue=0, variable=self.notimevar) miscframe.notime.grid(row=5,column=0,sticky='w') # Write tree self.writetreevar = Tkinter.IntVar() miscframe.writetree = \ Tkinter.Checkbutton(miscframe, text=u"Write tree", onvalue=1, offvalue=0, variable=self.writetreevar) miscframe.writetree.grid(row=6,column=0,sticky='w') # No_pairwise self.nopairwisevar = Tkinter.IntVar() miscframe.nopairwise = \ Tkinter.Checkbutton(miscframe, text=u"No pairwise", onvalue=1, offvalue=0, variable=self.nopairwisevar) miscframe.nopairwise.grid(row=7,column=0,sticky='w') # Collapse self.collapsevar = Tkinter.IntVar() miscframe.collapse = \ Tkinter.Checkbutton(miscframe, text=u"Collapse corr", onvalue=1, offvalue=0, variable=self.collapsevar) miscframe.collapse.grid(row=8,column=0,sticky='w') def initialize_pvalueframe(self): """ Initialize the filtration controlboard """ board = self.nepart pframe = board.pframe # Add p-value checkboxes self.naivetext = Tkinter.StringVar() self.pVar = Tkinter.IntVar() self.pVar.set(1) self.pBVar = Tkinter.IntVar() self.pBHVar = Tkinter.IntVar() self.pstext = Tkinter.StringVar() self.pPWVar = Tkinter.IntVar() self.pEPWVar = Tkinter.IntVar() self.pEPWVar.set(1) self.pPermVar = Tkinter.IntVar() pframe.naivelab = \ Tkinter.Label(pframe, textvariable=self.naivetext) self.naivetext.set(u"Pop structure-naive filters") pframe.pNcheck = \ Tkinter.Checkbutton(pframe, text=u"Naive (Fisher's)", onvalue=1, offvalue=0, variable=self.pVar) pframe.pBcheck = \ Tkinter.Checkbutton(pframe, text=u"Bonferroni", onvalue=1, offvalue=0, variable=self.pBVar) pframe.pBHcheck = \ Tkinter.Checkbutton( pframe, text=u"Benjamini-Hochberg", onvalue=1, offvalue=0, variable=self.pBHVar) pframe.pslab = \ Tkinter.Label(pframe, textvariable=self.pstext) self.pstext.set(u"Pop structure-aware filters") pframe.pPWcheck = \ Tkinter.Checkbutton(pframe, text=u"Pairwise comparison (Best)", onvalue=1, offvalue=0, variable=self.pPWVar) pframe.pEPWcheck = \ Tkinter.Checkbutton(pframe, text=u"Pairwise comparison (Entire)", onvalue=1, offvalue=0, variable=self.pEPWVar) pframe.pPermcheck = \ Tkinter.Checkbutton(pframe, text=u"Empirical p-value (Permutation)", onvalue=1, offvalue=0, variable=self.pPermVar) pframe.naivelab.grid(column=0,row=0,sticky='w') pframe.pNcheck.grid(column=0,row=1,sticky='w') pframe.pBcheck.grid(column=0,row=2,sticky='w') pframe.pBHcheck.grid(column=0,row=3,sticky='w') pframe.pslab.grid(column=0,row=4,sticky='w') pframe.pPWcheck.grid(column=0,row=5,sticky='w') pframe.pEPWcheck.grid(column=0,row=6,sticky='w') pframe.pPermcheck.grid(column=0,row=7,sticky='w') # Add p-value entry cells self.pNaive = Tkinter.StringVar() self.pBonf = Tkinter.StringVar() self.pBH = Tkinter.StringVar() self.pPW = Tkinter.StringVar() self.pEPW = Tkinter.StringVar() self.pPerm = Tkinter.StringVar() pframe.pNaiveEntry = Tkinter.Entry(pframe, textvariable=self.pNaive, width=8) pframe.pBonfEntry = Tkinter.Entry(pframe, textvariable=self.pBonf, width=8) pframe.pBHEntry = Tkinter.Entry(pframe, textvariable=self.pBH, width=8) pframe.pPWEntry = Tkinter.Entry(pframe, textvariable=self.pPW, width=8) pframe.pEPWEntry = Tkinter.Entry(pframe, textvariable=self.pEPW, width=8) pframe.pPermEntry = Tkinter.Entry(pframe, textvariable=self.pPerm, width=8) pframe.pNaiveEntry.grid(column=1,row=1,sticky='w') pframe.pBonfEntry.grid(column=1,row=2,sticky='w') pframe.pBHEntry.grid(column=1,row=3,sticky='w') pframe.pPWEntry.grid(column=1,row=5,sticky='w') pframe.pEPWEntry.grid(column=1,row=6,sticky='w') pframe.pPermEntry.grid(column=1,row=7,sticky='w') self.pNaive.set("0.05") self.pBonf.set("1.0") self.pBH.set("1.0") self.pPW.set("1.0") self.pEPW.set("0.05") self.pPerm.set("1.0") ######## EVENTS ######## def AboutScoary(self): """ Placeholder button. Planned short information about the method """ topwin = Tkinter.Toplevel(self) button = \ Tkinter.Button(topwin, text=str("https://github.com/AdmiralenOla/Scoary")) button.pack() def BrowseButtonClickGPA(self): """ Browse button for gene presence absence field """ myfile = \ tkFileDialog.askopenfilename( filetypes=[('comma-separated values', '.csv'), ('all files','.*')]) self.GPAentryVariable.set(myfile) def BrowseButtonClickTraits(self): """ Browse button for traits field """ myfile = \ tkFileDialog.askopenfilename( filetypes=[('comma-separated values', '.csv'), ('all files','.*')]) self.TraitsentryVariable.set(myfile) def BrowseButtonClickTreeFile(self): """ Browse button for tree field """ myfile = \ tkFileDialog.askopenfilename( filetypes=[('newick tree files', '.nwk'), ('all files','.*')]) self.TreeentryVariable.set(myfile) def BrowseButtonClickRestrict(self): """ Browse button for isolate restriction field """ myfile = \ tkFileDialog.askopenfilename( filetypes=[('comma-separated values','.csv'), ('all files','.*')]) self.RestrictVariable.set(myfile) def BrowseButtonClickOutput(self): """ Browse button for choosing output dir """ mydir = tkFileDialog.askdirectory(mustexist=True) self.Outputdir.set(mydir) def HelpButton(self): """ Placeholder button. Redirects to website """ print("Visit https://github.com/AdmiralenOla/Scoary for help") def ClearAll(self): """ Sets all variables to defaults """ self.GPAentryVariable.set("Path to gene presence absence file") self.TraitsentryVariable.set("Path to traits/phenotype file") self.TreeentryVariable.set("(Optional) Path to custom tree " "file") self.RestrictVariable.set("(Optional) Path to file naming " "isolates to include") self.Outputdir.set("") self.maxhitsvar.set("") self.delimvar.set(",") self.scvar.set("15") self.notimevar.set(0) self.writetreevar.set(0) self.permvar.set("0") self.pVar.set(1) self.pBVar.set(0) self.pBHVar.set(0) self.pPWVar.set(0) self.pEPWVar.set(1) self.pNaive.set("0.05") self.pBonf.set("1.0") self.pBH.set("1.0") self.pPW.set("1.0") self.pEPW.set("0.05") self.pPerm.set("1.0") self.nopairwisevar.set(0) def TestExample(self): """ Sets all variables corresponding to --test in the methods script """ self.GPAentryVariable.set( str(os.path.join(resource_filename(__name__, 'exampledata'), 'Gene_presence_absence.csv'))) self.TraitsentryVariable.set( str(os.path.join(resource_filename(__name__, 'exampledata'), 'Tetracycline_resistance.csv'))) self.TreeentryVariable.set("") self.RestrictVariable.set("") self.Outputdir.set("./") self.maxhitsvar.set("") self.delimvar.set(",") self.scvar.set("15") self.notimevar.set(0) self.writetreevar.set(0) self.permvar.set("0") self.pVar.set(1) self.pBVar.set(0) self.pBHVar.set(0) self.pPWVar.set(0) self.pEPWVar.set(1) self.pNaive.set("0.05") self.pBonf.set("1.0") self.pBH.set("1.0") self.pPW.set("1.0") self.pEPW.set("0.05") self.pPerm.set("1.0") self.nopairwisevar.set(0) ######## RUNNING THE ANALYSIS ######## def RunAnalysis(self): """ Upon click "Run analysis" - Reads all the set parameters and calls run method """ self.Scoary_parameters["GPA"] = self.GPAentryVariable.get() self.Scoary_parameters["Traits"] = \ self.TraitsentryVariable.get() self.Scoary_parameters["Tree"] = self.TreeentryVariable.get() self.Scoary_parameters["Outdir"] = self.Outputdir.get() self.Scoary_parameters["Restrict"] = self.RestrictVariable.get() self.Scoary_parameters["Writetree"] = self.writetreevar.get() self.Scoary_parameters["Delimiter"] = self.delimvar.get() self.Scoary_parameters["Startcol"] = self.scvar.get() self.Scoary_parameters["Maxhits"] = self.maxhitsvar.get() self.Scoary_parameters["Notime"] = self.notimevar.get() self.Scoary_parameters["Permutations"] = self.permvar.get() self.Scoary_parameters["No_pairwise"] = self.nopairwisevar.get() self.Scoary_parameters["Collapse"] = self.collapsevar.get() self.Scoary_parameters["Cutoffs"]["I"][0] = self.pVar.get() self.Scoary_parameters["Cutoffs"]["I"][1] = self.pNaive.get() self.Scoary_parameters["Cutoffs"]["B"][0] = self.pBVar.get() self.Scoary_parameters["Cutoffs"]["B"][1] = self.pBonf.get() self.Scoary_parameters["Cutoffs"]["BH"][0] = self.pBHVar.get() self.Scoary_parameters["Cutoffs"]["BH"][1] = self.pBH.get() self.Scoary_parameters["Cutoffs"]["PW"][0] = self.pPWVar.get() self.Scoary_parameters["Cutoffs"]["PW"][1] = self.pPW.get() self.Scoary_parameters["Cutoffs"]["EPW"][0] = self.pEPWVar.get() self.Scoary_parameters["Cutoffs"]["EPW"][1] = self.pEPW.get() self.Scoary_parameters["Cutoffs"]["P"][0] = self.pPermVar.get() self.Scoary_parameters["Cutoffs"]["P"][1] = self.pPerm.get() self.PrepareScoaryCMDline() def PrepareScoaryCMDline(self): """ Prepares arguments to correspond with argparse namespace and runs. Listens to sys.stdout and updates statusbar. """ citation=False RunScoary = True correction = [] p_value_cutoff = [] for m in self.Scoary_parameters["Cutoffs"]: if self.Scoary_parameters["Cutoffs"][m][0] == 1: correction.append(m) try: p_value_cutoff.append( float(self.Scoary_parameters["Cutoffs"][m][1])) except ValueError: print("Please enter real numbers in the p value " "fields") RunScoary = False break delimiter = self.Scoary_parameters["Delimiter"] genes = (self.Scoary_parameters["GPA"] if self.Scoary_parameters["GPA"] not in ["","Path to gene presence absence file"] else None) try: max_hits = (int(self.Scoary_parameters["Maxhits"]) if self.Scoary_parameters["Maxhits"] not in [""] else None) except ValueError: print("Please enter a real number (or nothing) in the max " "hits field") max_hits = None RunScoary = False try: permutations = (abs(int( self.Scoary_parameters["Permutations"])) if self.Scoary_parameters["Permutations"] not in [""] else 0) except ValueError: print("Please enter a real number (or nothing) in the max " "hits field") permutations = 0 RunScoary = False newicktree = (self.Scoary_parameters["Tree"] if self.Scoary_parameters["Tree"] not in ["", "(Optional) Path to custom tree file"] else None) no_time = (True if self.Scoary_parameters["Notime"] == 1 else False) outdir = (self.Scoary_parameters["Outdir"] if self.Scoary_parameters["Outdir"] not in ["","(Optional) Output directory"] else "./") restrict_to = (self.Scoary_parameters["Restrict"] if self.Scoary_parameters["Restrict"] not in ["","(Optional) Path to file naming isolates to " "include"] else None) start_col = self.Scoary_parameters["Startcol"] test = False threads = 1 traits = (self.Scoary_parameters["Traits"] if self.Scoary_parameters["Traits"] not in ["","Path to traits/phenotype file"] else None) upgma_tree = (True if self.Scoary_parameters["Writetree"] == 1 else False) write_reduced=False collapse= (True if self.Scoary_parameters["Collapse"] == 1 else False) no_pairwise = (True if self.Scoary_parameters["No_pairwise"] == 1 else False) myargs = argparse.Namespace(citation=citation, correction=correction, p_value_cutoff=p_value_cutoff, delimiter=delimiter, genes=genes, grabcols=[], max_hits=max_hits, newicktree=newicktree, no_time=no_time, restrict_to=restrict_to, outdir=outdir, permute=permutations, start_col=start_col, test=test, threads=threads, traits=traits, upgma_tree=upgma_tree, write_reduced=write_reduced, collapse=collapse, no_pairwise=no_pairwise) if RunScoary: try: sm.main(args=myargs, cutoffs=dict( list(zip(correction, p_value_cutoff))), statusbar=sys.stdout) except SystemExit as SE: # Set status bar color to red? if str(SE) == "0": print("Analysis complete!") else: print("Fatal error: %s" % str(SE)) # Listen to stdout and update statusbar ######## MISC METHODS ######## def initialize_logo(self): """ Initialize logo """ photo=Tkinter.PhotoImage(data=self.Photobase64()) self.logocanvas.img = photo self.logocanvas.create_image(0,0,anchor='nw',image=photo) def citationtext(self): """ Returns citation info """ text = "SCOARY version %s \n\n" \ "Please cite as: \n" \ "Brynildsrud O, Bohlin J, Scheffer L, \n" \ "Eldholm V. Rapid scoring of genes in \n" \ "microbial pan-genome-wide association \n" \ "studies with Scoary. Genome Biol. \n" \ "2016;17:238" % scoary.__version__ return text def Photobase64(self): """ base64 encoding of the logo """ photo= """ R0lGODlh+gD6AOf/AAABAAEEAAMGAgcJBQoMCAwPCw8RDhIUERYYFhkbGRwdGx8g HiIkISYoJSstKjsrLkArL0gqJ00pI0QsJzEyMEQtLT8wMkYvL0IxLkUwM0svMDQ2 M0IyNE0wMUQzMEMzNUoyMjY4NUQ1N0Y1MkI3ODk7OEc3OUk5PEU7PD4/PU07OUU9 QkE/Q04+QD9DRUFDQERETVBBSEtDSE1DQ0dFSEZHRVRDRlZDQUFJUUlLSD1PWVRK SjhRWkxOSzRVY1xMUEdSXj5VZFBST0BWYFNVUjRcbl9VWFZYVWJVUT9ebEtdaDZj dVtdWkBjd2lcVyNsh2hcXSxrgV9hXjlsg2VnZDV0inFmZzF3kmpsaXZpZyl8lh5/ nit9nm5wbSeEo3hzcjiCnXN1ch6JroBzcnZ4dYF2di+KqRaQuoB4cnh6dyeOs4d5 dBiUuASaw3x+e4CBf4mBeyyXwACj0wCh8YyFgIWHhJCEhBugygCm8ACo8QCp8h6l zhGo2ACr7h+i7QCr9IuNigCu4wCw3gqt8I6QjRyr2wCy9BCu8QC16ZqSjJKUkQC2 8Q2z6QC2+Bew8wC59AC67wC6/Be166GWip6XkQC895eZlgC9/iC18qSclSa49QjB 9qKdnJ2fnBDA+yi78aWgny2+9KKkoTLA9qWnpKynpamrqEfD9Kyuq7OtqjvJ+EvG 97iysbK0sE7M9lvK9be5tl/N+bu9umDS9768wMS8tWzQ9sDCv8bBwHvT+3rW94XW +MbIxM/HwHza+4fY+snLyM7KzIjd95Hb+M3PzJXf/J3e/NfSytjS0dPV0ZHl+aXg +drWx57j+tXX1Kjj/N3a0dnb2Kjn+bLo+7nn++Te2t7h3ePi2bvu+8Ps+uPl4ubm 3Mbv/c3t/u7o5+jq5svy+uzr4tTx++3s4+zu6/Pt7Nj0/+7w7d7z//Hw5t32++T1 /PL08Pjz8ff27ej5//T38/D4/+z6+/P4++77/PX6/fj69/r8+ff9//n+///9+/z+ +//9////9v7//P///yH+C1Njb2FyeSBsb2dvACH5BAEKAP8ALAAAAAD6APoAAAj+ AP0JHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOq XMmypcuXMGPKnEmzps2bOHPq3Mmzp8+fQIMKHUq0qNGjSJMqXcq0qdOnUKNKnUq1 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 """ return photo class StdoutToLabel(Tkinter.Label): """ The special widget that listens to sys.stdout and updates its label accordingly. This widget also owns the progressbar it is placed on and updates it every time sys.stdout is called. Use stop() to stop retrieving from sys.stdout. Credit: http://stackoverflow.com/questions/2914603/ """ def __init__(self, widget, progressbar=None, width='default'): Tkinter.Label.__init__(self) self.defstdout = sys.stdout self.widget = widget self.progressbar = progressbar if width == 'default': try: self.width = widget.cget('width') except: self.width = None else: self.width = width def flush(self): """ Frame sys.stdout's flush method. """ self.defstdout.flush() def write(self, string, end=None): """ Frame sys.stdout's write method. Puts input strings in the widget. """ self.defstdout.write(string) if string == "\n": pass elif (len(string) > 100 and self.width is not None and self.width > 0): if ("Fatal error" in string): self.widget.config(text= "Fatal error: See log file for details") else: self.widget.config(text="See log file for details") elif string is not None: last_char = string[-1] if last_char != '\n' and string.startswith('\r'): number = string.lstrip("\r").rstrip("%") number = number.split(".")[0] number = int(number) self.progressbar["value"] = number elif last_char != '\n': self.widget.config(text=string) elif last_char == '\n' and string.startswith('\r'): number = string.lstrip("\r").rstrip("%") number = number.split(".")[0] number = int(number) self.progressbar["value"] = number else: self.widget.config(text=string[:-1]) self.update() self.progressbar.update() def start(self): """ Starts retrieving. """ sys.stdout = self def stop(self): """ Stops retrieving. """ sys.stdout = self.defstdout @property def errors(self): return self.defstdout.errors @property def encoding(self): return self.defstdout.encoding def main(): root = ScoaryGUI(None) root.title("Scoary") root.geometry("800x400") root.resizable(0,0) root.mainloop() if __name__ == "__main__": pass