| OLD | NEW |
| (Empty) |
| 1 """Post-install GUI to compile to pyc and unpack twisted doco""" | |
| 2 | |
| 3 from __future__ import generators | |
| 4 | |
| 5 import sys | |
| 6 import zipfile | |
| 7 import py_compile | |
| 8 | |
| 9 # we're going to ignore failures to import tkinter and fall back | |
| 10 # to using the console if the required dll is not found | |
| 11 | |
| 12 # Scary kludge to work around tk84.dll bug: | |
| 13 # https://sourceforge.net/tracker/index.php?func=detail&aid=814654&group_id=5470
&atid=105470 | |
| 14 # Without which(): you get a windows missing-dll popup message | |
| 15 from twisted.python.procutils import which | |
| 16 tkdll='tk84.dll' | |
| 17 if which(tkdll) or which('DLLs/%s' % tkdll): | |
| 18 try: | |
| 19 import Tkinter | |
| 20 from Tkinter import * | |
| 21 from twisted.internet import tksupport | |
| 22 except ImportError: | |
| 23 pass | |
| 24 | |
| 25 # twisted | |
| 26 from twisted.internet import reactor, defer | |
| 27 from twisted.python import failure, log, zipstream, util, usage, log | |
| 28 # local | |
| 29 import os.path | |
| 30 | |
| 31 class ProgressBar: | |
| 32 def __init__(self, master=None, orientation="horizontal", | |
| 33 min=0, max=100, width=100, height=18, | |
| 34 doLabel=1, appearance="sunken", | |
| 35 fillColor="blue", background="gray", | |
| 36 labelColor="yellow", labelFont="Arial", | |
| 37 labelText="", labelFormat="%d%%", | |
| 38 value=0, bd=2): | |
| 39 # preserve various values | |
| 40 self.master=master | |
| 41 self.orientation=orientation | |
| 42 self.min=min | |
| 43 self.max=max | |
| 44 self.width=width | |
| 45 self.height=height | |
| 46 self.doLabel=doLabel | |
| 47 self.fillColor=fillColor | |
| 48 self.labelFont= labelFont | |
| 49 self.labelColor=labelColor | |
| 50 self.background=background | |
| 51 self.labelText=labelText | |
| 52 self.labelFormat=labelFormat | |
| 53 self.value=value | |
| 54 self.frame=Frame(master, relief=appearance, bd=bd) | |
| 55 self.canvas=Canvas(self.frame, height=height, width=width, bd=0, | |
| 56 highlightthickness=0, background=background) | |
| 57 self.scale=self.canvas.create_rectangle(0, 0, width, height, | |
| 58 fill=fillColor) | |
| 59 self.label=self.canvas.create_text(self.canvas.winfo_reqwidth() / 2, | |
| 60 height / 2, text=labelText, | |
| 61 anchor="c", fill=labelColor, | |
| 62 font=self.labelFont) | |
| 63 self.update() | |
| 64 self.canvas.pack(side='top', fill='x', expand='no') | |
| 65 | |
| 66 def pack(self, *args, **kwargs): | |
| 67 self.frame.pack(*args, **kwargs) | |
| 68 | |
| 69 def updateProgress(self, newValue, newMax=None): | |
| 70 if newMax: | |
| 71 self.max = newMax | |
| 72 self.value = newValue | |
| 73 self.update() | |
| 74 | |
| 75 def update(self): | |
| 76 # Trim the values to be between min and max | |
| 77 value=self.value | |
| 78 if value > self.max: | |
| 79 value = self.max | |
| 80 if value < self.min: | |
| 81 value = self.min | |
| 82 # Adjust the rectangle | |
| 83 if self.orientation == "horizontal": | |
| 84 self.canvas.coords(self.scale, 0, 0, | |
| 85 float(value) / self.max * self.width, self.height) | |
| 86 else: | |
| 87 self.canvas.coords(self.scale, 0, | |
| 88 self.height - (float(value) / | |
| 89 self.max*self.height), | |
| 90 self.width, self.height) | |
| 91 # Now update the colors | |
| 92 self.canvas.itemconfig(self.scale, fill=self.fillColor) | |
| 93 self.canvas.itemconfig(self.label, fill=self.labelColor) | |
| 94 # And update the label | |
| 95 if self.doLabel: | |
| 96 if value: | |
| 97 if value >= 0: | |
| 98 pvalue = int((float(value) / float(self.max)) * | |
| 99 100.0) | |
| 100 else: | |
| 101 pvalue = 0 | |
| 102 self.canvas.itemconfig(self.label, text=self.labelFormat | |
| 103 % pvalue) | |
| 104 else: | |
| 105 self.canvas.itemconfig(self.label, text='') | |
| 106 else: | |
| 107 self.canvas.itemconfig(self.label, text=self.labelFormat % | |
| 108 self.labelText) | |
| 109 self.canvas.update_idletasks() | |
| 110 | |
| 111 | |
| 112 class Progressor: | |
| 113 """A base class to make it simple to hook a progress bar up to a process. | |
| 114 """ | |
| 115 def __init__(self, title, *args, **kwargs): | |
| 116 self.title=title | |
| 117 self.stopping=0 | |
| 118 self.bar=None | |
| 119 self.iterator=None | |
| 120 self.remaining=1000 | |
| 121 | |
| 122 def setBar(self, bar, max): | |
| 123 self.bar=bar | |
| 124 bar.updateProgress(0, max) | |
| 125 return self | |
| 126 | |
| 127 def setIterator(self, iterator): | |
| 128 self.iterator=iterator | |
| 129 return self | |
| 130 | |
| 131 def updateBar(self, deferred): | |
| 132 b=self.bar | |
| 133 try: | |
| 134 b.updateProgress(b.max - self.remaining) | |
| 135 except TclError: | |
| 136 self.stopping=1 | |
| 137 except: | |
| 138 deferred.errback(failure.Failure()) | |
| 139 | |
| 140 def processAll(self, root): | |
| 141 assert self.bar and self.iterator, "must setBar and setIterator" | |
| 142 self.root=root | |
| 143 root.title(self.title) | |
| 144 d=defer.Deferred() | |
| 145 d.addErrback(log.err) | |
| 146 reactor.callLater(0.1, self.processOne, d) | |
| 147 return d | |
| 148 | |
| 149 def processOne(self, deferred): | |
| 150 if self.stopping: | |
| 151 deferred.callback(self.root) | |
| 152 return | |
| 153 | |
| 154 try: | |
| 155 self.remaining=self.iterator.next() | |
| 156 except StopIteration: | |
| 157 self.stopping=1 | |
| 158 except: | |
| 159 deferred.errback(failure.Failure()) | |
| 160 | |
| 161 if self.remaining%10==0: | |
| 162 reactor.callLater(0, self.updateBar, deferred) | |
| 163 if self.remaining%100==0: | |
| 164 log.msg(self.remaining) | |
| 165 reactor.callLater(0, self.processOne, deferred) | |
| 166 | |
| 167 def compiler(path): | |
| 168 """A generator for compiling files to .pyc""" | |
| 169 def justlist(arg, directory, names): | |
| 170 pynames=[os.path.join(directory, n) for n in names | |
| 171 if n.endswith('.py')] | |
| 172 arg.extend(pynames) | |
| 173 all=[] | |
| 174 os.path.walk(path, justlist, all) | |
| 175 | |
| 176 remaining=len(all) | |
| 177 i=zip(all, range(remaining-1, -1, -1)) | |
| 178 for f, remaining in i: | |
| 179 py_compile.compile(f) | |
| 180 yield remaining | |
| 181 | |
| 182 class TkunzipOptions(usage.Options): | |
| 183 optParameters=[["zipfile", "z", "", "a zipfile"], | |
| 184 ["ziptargetdir", "t", ".", "where to extract zipfile"], | |
| 185 ["compiledir", "c", "", "a directory to compile"], | |
| 186 ] | |
| 187 optFlags=[["use-console", "C", "show in the console, not graphically"], | |
| 188 ["shell-exec", "x", """\ | |
| 189 spawn a new console to show output (implies -C)"""], | |
| 190 ] | |
| 191 | |
| 192 def countPys(countl, directory, names): | |
| 193 sofar=countl[0] | |
| 194 sofar=sofar+len([f for f in names if f.endswith('.py')]) | |
| 195 countl[0]=sofar | |
| 196 return sofar | |
| 197 | |
| 198 def countPysRecursive(path): | |
| 199 countl=[0] | |
| 200 os.path.walk(path, countPys, countl) | |
| 201 return countl[0] | |
| 202 | |
| 203 def run(argv=sys.argv): | |
| 204 log.startLogging(file('tkunzip.log', 'w')) | |
| 205 opt=TkunzipOptions() | |
| 206 try: | |
| 207 opt.parseOptions(argv[1:]) | |
| 208 except usage.UsageError, e: | |
| 209 print str(opt) | |
| 210 print str(e) | |
| 211 sys.exit(1) | |
| 212 | |
| 213 if opt['use-console']: | |
| 214 # this should come before shell-exec to prevent infinite loop | |
| 215 return doItConsolicious(opt) | |
| 216 if opt['shell-exec'] or not 'Tkinter' in sys.modules: | |
| 217 from distutils import sysconfig | |
| 218 from twisted.scripts import tkunzip | |
| 219 myfile=tkunzip.__file__ | |
| 220 exe=os.path.join(sysconfig.get_config_var('prefix'), 'python.exe') | |
| 221 return os.system('%s %s --use-console %s' % (exe, myfile, | |
| 222 ' '.join(argv[1:]))) | |
| 223 return doItTkinterly(opt) | |
| 224 | |
| 225 def doItConsolicious(opt): | |
| 226 # reclaim stdout/stderr from log | |
| 227 sys.stdout = sys.__stdout__ | |
| 228 sys.stderr = sys.__stderr__ | |
| 229 if opt['zipfile']: | |
| 230 print 'Unpacking documentation...' | |
| 231 for n in zipstream.unzipIter(opt['zipfile'], opt['ziptargetdir']): | |
| 232 if n % 100 == 0: | |
| 233 print n, | |
| 234 if n % 1000 == 0: | |
| 235 print | |
| 236 print 'Done unpacking.' | |
| 237 | |
| 238 if opt['compiledir']: | |
| 239 print 'Compiling to pyc...' | |
| 240 import compileall | |
| 241 compileall.compile_dir(opt["compiledir"]) | |
| 242 print 'Done compiling.' | |
| 243 | |
| 244 def doItTkinterly(opt): | |
| 245 root=Tkinter.Tk() | |
| 246 root.withdraw() | |
| 247 root.title('One Moment.') | |
| 248 root.protocol('WM_DELETE_WINDOW', reactor.stop) | |
| 249 tksupport.install(root) | |
| 250 | |
| 251 prog=ProgressBar(root, value=0, labelColor="black", width=200) | |
| 252 prog.pack() | |
| 253 | |
| 254 # callback immediately | |
| 255 d=defer.succeed(root).addErrback(log.err) | |
| 256 | |
| 257 def deiconify(root): | |
| 258 root.deiconify() | |
| 259 return root | |
| 260 | |
| 261 d.addCallback(deiconify) | |
| 262 | |
| 263 if opt['zipfile']: | |
| 264 uz=Progressor('Unpacking documentation...') | |
| 265 max=zipstream.countZipFileChunks(opt['zipfile'], 4096) | |
| 266 uz.setBar(prog, max) | |
| 267 uz.setIterator(zipstream.unzipIterChunky(opt['zipfile'], | |
| 268 opt['ziptargetdir'])) | |
| 269 d.addCallback(uz.processAll) | |
| 270 | |
| 271 if opt['compiledir']: | |
| 272 comp=Progressor('Compiling to pyc...') | |
| 273 comp.setBar(prog, countPysRecursive(opt['compiledir'])) | |
| 274 comp.setIterator(compiler(opt['compiledir'])) | |
| 275 d.addCallback(comp.processAll) | |
| 276 | |
| 277 def stop(ignore): | |
| 278 reactor.stop() | |
| 279 root.destroy() | |
| 280 d.addCallback(stop) | |
| 281 | |
| 282 reactor.run() | |
| 283 | |
| 284 | |
| 285 if __name__=='__main__': | |
| 286 run() | |
| OLD | NEW |