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 |