1 # -*- test-case-name: twisted.scripts.test.test_scripts -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
6 Post-install GUI to compile to pyc and unpack twisted doco
9 from __future__ import generators
15 # we're going to ignore failures to import tkinter and fall back
16 # to using the console if the required dll is not found
18 # Scary kludge to work around tk84.dll bug:
19 # https://sourceforge.net/tracker/index.php?func=detail&aid=814654&group_id=5470&atid=105470
20 # Without which(): you get a windows missing-dll popup message
21 from twisted.python.procutils import which
23 if which(tkdll) or which('DLLs/%s' % tkdll):
27 from twisted.internet import tksupport
32 from twisted.internet import reactor, defer
33 from twisted.python import failure, log, zipstream, util, usage, log
38 def __init__(self, master=None, orientation="horizontal",
39 min=0, max=100, width=100, height=18,
40 doLabel=1, appearance="sunken",
41 fillColor="blue", background="gray",
42 labelColor="yellow", labelFont="Arial",
43 labelText="", labelFormat="%d%%",
45 # preserve various values
47 self.orientation=orientation
53 self.fillColor=fillColor
54 self.labelFont= labelFont
55 self.labelColor=labelColor
56 self.background=background
57 self.labelText=labelText
58 self.labelFormat=labelFormat
60 self.frame=Frame(master, relief=appearance, bd=bd)
61 self.canvas=Canvas(self.frame, height=height, width=width, bd=0,
62 highlightthickness=0, background=background)
63 self.scale=self.canvas.create_rectangle(0, 0, width, height,
65 self.label=self.canvas.create_text(self.canvas.winfo_reqwidth() / 2,
66 height / 2, text=labelText,
67 anchor="c", fill=labelColor,
70 self.canvas.pack(side='top', fill='x', expand='no')
72 def pack(self, *args, **kwargs):
73 self.frame.pack(*args, **kwargs)
75 def updateProgress(self, newValue, newMax=None):
82 # Trim the values to be between min and max
88 # Adjust the rectangle
89 if self.orientation == "horizontal":
90 self.canvas.coords(self.scale, 0, 0,
91 float(value) / self.max * self.width, self.height)
93 self.canvas.coords(self.scale, 0,
94 self.height - (float(value) /
95 self.max*self.height),
96 self.width, self.height)
97 # Now update the colors
98 self.canvas.itemconfig(self.scale, fill=self.fillColor)
99 self.canvas.itemconfig(self.label, fill=self.labelColor)
100 # And update the label
104 pvalue = int((float(value) / float(self.max)) *
108 self.canvas.itemconfig(self.label, text=self.labelFormat
111 self.canvas.itemconfig(self.label, text='')
113 self.canvas.itemconfig(self.label, text=self.labelFormat %
115 self.canvas.update_idletasks()
119 """A base class to make it simple to hook a progress bar up to a process.
121 def __init__(self, title, *args, **kwargs):
128 def setBar(self, bar, max):
130 bar.updateProgress(0, max)
133 def setIterator(self, iterator):
134 self.iterator=iterator
137 def updateBar(self, deferred):
140 b.updateProgress(b.max - self.remaining)
144 deferred.errback(failure.Failure())
146 def processAll(self, root):
147 assert self.bar and self.iterator, "must setBar and setIterator"
149 root.title(self.title)
151 d.addErrback(log.err)
152 reactor.callLater(0.1, self.processOne, d)
155 def processOne(self, deferred):
157 deferred.callback(self.root)
161 self.remaining=self.iterator.next()
162 except StopIteration:
165 deferred.errback(failure.Failure())
167 if self.remaining%10==0:
168 reactor.callLater(0, self.updateBar, deferred)
169 if self.remaining%100==0:
170 log.msg(self.remaining)
171 reactor.callLater(0, self.processOne, deferred)
174 """A generator for compiling files to .pyc"""
175 def justlist(arg, directory, names):
176 pynames=[os.path.join(directory, n) for n in names
177 if n.endswith('.py')]
180 os.path.walk(path, justlist, all)
183 i=zip(all, range(remaining-1, -1, -1))
184 for f, remaining in i:
185 py_compile.compile(f)
188 class TkunzipOptions(usage.Options):
189 optParameters=[["zipfile", "z", "", "a zipfile"],
190 ["ziptargetdir", "t", ".", "where to extract zipfile"],
191 ["compiledir", "c", "", "a directory to compile"],
193 optFlags=[["use-console", "C", "show in the console, not graphically"],
194 ["shell-exec", "x", """\
195 spawn a new console to show output (implies -C)"""],
198 def countPys(countl, directory, names):
200 sofar=sofar+len([f for f in names if f.endswith('.py')])
204 def countPysRecursive(path):
206 os.path.walk(path, countPys, countl)
209 def run(argv=sys.argv):
210 log.startLogging(file('tkunzip.log', 'w'))
213 opt.parseOptions(argv[1:])
214 except usage.UsageError, e:
219 if opt['use-console']:
220 # this should come before shell-exec to prevent infinite loop
221 return doItConsolicious(opt)
222 if opt['shell-exec'] or not 'Tkinter' in sys.modules:
223 from distutils import sysconfig
224 from twisted.scripts import tkunzip
225 myfile=tkunzip.__file__
226 exe=os.path.join(sysconfig.get_config_var('prefix'), 'python.exe')
227 return os.system('%s %s --use-console %s' % (exe, myfile,
229 return doItTkinterly(opt)
231 def doItConsolicious(opt):
232 # reclaim stdout/stderr from log
233 sys.stdout = sys.__stdout__
234 sys.stderr = sys.__stderr__
236 print 'Unpacking documentation...'
237 for n in zipstream.unzipIter(opt['zipfile'], opt['ziptargetdir']):
242 print 'Done unpacking.'
244 if opt['compiledir']:
245 print 'Compiling to pyc...'
247 compileall.compile_dir(opt["compiledir"])
248 print 'Done compiling.'
250 def doItTkinterly(opt):
253 root.title('One Moment.')
254 root.protocol('WM_DELETE_WINDOW', reactor.stop)
255 tksupport.install(root)
257 prog=ProgressBar(root, value=0, labelColor="black", width=200)
260 # callback immediately
261 d=defer.succeed(root).addErrback(log.err)
267 d.addCallback(deiconify)
270 uz=Progressor('Unpacking documentation...')
271 max=zipstream.countZipFileChunks(opt['zipfile'], 4096)
273 uz.setIterator(zipstream.unzipIterChunky(opt['zipfile'],
274 opt['ziptargetdir']))
275 d.addCallback(uz.processAll)
277 if opt['compiledir']:
278 comp=Progressor('Compiling to pyc...')
279 comp.setBar(prog, countPysRecursive(opt['compiledir']))
280 comp.setIterator(compiler(opt['compiledir']))
281 d.addCallback(comp.processAll)
291 if __name__=='__main__':