1
2
3
4
5
6
7
8
9
10 """
11 Graphical interface to epydoc. This interface might be useful for
12 systems where it's inconvenient to use the command-line interface
13 (such as Windows). It supports many (but not all) of the features
14 that are supported by the command-line interface. It also supports
15 loading and saving of X{project files}, which store a set of related
16 modules, and the options that should be used to generate the
17 documentation for those modules.
18
19 Usage::
20 epydocgui [OPTIONS] [FILE.prj | MODULES...]
21
22 FILE.prj An epydoc GUI project file.
23 MODULES... A list of Python modules to document.
24 -V, --version Print the version of epydoc.
25 -h, -?, --help, --usage Display this usage message
26 --debug Do not suppress error messages
27
28 @todo: Use ini-style project files, rather than pickles (using the
29 same format as the CLI).
30 """
31 __docformat__ = 'epytext en'
32
33 import sys, os.path, re, glob
34 from Tkinter import *
35 from tkFileDialog import askopenfilename, asksaveasfilename
36 from thread import start_new_thread, exit_thread
37 from pickle import dump, load
38
39
40
41 try: from tkFileDialog import askdirectory
42 except: askdirectory = None
43
44
45 try: import ZODB
46 except: pass
47
48
49
50
51
52 DEBUG = 0
53
54
55 BG_COLOR='#e0e0e0'
56 ACTIVEBG_COLOR='#e0e0e0'
57 TEXT_COLOR='black'
58 ENTRYSELECT_COLOR = ACTIVEBG_COLOR
59 SELECT_COLOR = '#208070'
60 MESSAGE_COLOR = '#000060'
61 ERROR_COLOR = '#600000'
62 GUIERROR_COLOR = '#600000'
63 WARNING_COLOR = '#604000'
64 HEADER_COLOR = '#000000'
65
66
67 COLOR_CONFIG = {'background':BG_COLOR, 'highlightcolor': BG_COLOR,
68 'foreground':TEXT_COLOR, 'highlightbackground': BG_COLOR}
69 ENTRY_CONFIG = {'background':BG_COLOR, 'highlightcolor': BG_COLOR,
70 'foreground':TEXT_COLOR, 'highlightbackground': BG_COLOR,
71 'selectbackground': ENTRYSELECT_COLOR,
72 'selectforeground': TEXT_COLOR}
73 SB_CONFIG = {'troughcolor':BG_COLOR, 'activebackground':BG_COLOR,
74 'background':BG_COLOR, 'highlightbackground':BG_COLOR}
75 LISTBOX_CONFIG = {'highlightcolor': BG_COLOR, 'highlightbackground': BG_COLOR,
76 'foreground':TEXT_COLOR, 'selectforeground': TEXT_COLOR,
77 'selectbackground': ACTIVEBG_COLOR, 'background':BG_COLOR}
78 BUTTON_CONFIG = {'background':BG_COLOR, 'highlightthickness':0, 'padx':4,
79 'highlightbackground': BG_COLOR, 'foreground':TEXT_COLOR,
80 'highlightcolor': BG_COLOR, 'activeforeground': TEXT_COLOR,
81 'activebackground': ACTIVEBG_COLOR, 'pady':0}
82 CBUTTON_CONFIG = {'background':BG_COLOR, 'highlightthickness':0, 'padx':4,
83 'highlightbackground': BG_COLOR, 'foreground':TEXT_COLOR,
84 'highlightcolor': BG_COLOR, 'activeforeground': TEXT_COLOR,
85 'activebackground': ACTIVEBG_COLOR, 'pady':0,
86 'selectcolor': SELECT_COLOR}
87 SHOWMSG_CONFIG = CBUTTON_CONFIG.copy()
88 SHOWMSG_CONFIG['foreground'] = MESSAGE_COLOR
89 SHOWWRN_CONFIG = CBUTTON_CONFIG.copy()
90 SHOWWRN_CONFIG['foreground'] = WARNING_COLOR
91 SHOWERR_CONFIG = CBUTTON_CONFIG.copy()
92 SHOWERR_CONFIG['foreground'] = ERROR_COLOR
93
94
95 PROGRESS_HEIGHT = 16
96 PROGRESS_WIDTH = 200
97 PROGRESS_BG='#305060'
98 PROGRESS_COLOR1 = '#30c070'
99 PROGRESS_COLOR2 = '#60ffa0'
100 PROGRESS_COLOR3 = '#106030'
101
102
103 if sys.platform.lower().startswith('win'):
104 DX = 3; DY = 3
105 DH = 0; DW = 7
106 else:
107 DX = 1; DY = 1
108 DH = 1; DW = 3
109
110
111 IMPORT_PROGRESS = 0.1
112 BUILD_PROGRESS = 0.2
113 WRITE_PROGRESS = 1.0 - BUILD_PROGRESS - IMPORT_PROGRESS
114
115
116
117
118
119 UP_GIF = '''\
120 R0lGODlhCwAMALMAANnZ2QDMmQCZZgBmZgAAAAAzM////////wAAAAAAAAAAAAAAAAAAAAAAAAAA
121 AAAAACH5BAEAAAAALAAAAAALAAwAAAQjEMhJKxCW4gzCIJxXZIEwFGDlDadqsii1sq1U0nA64+ON
122 5xEAOw==
123 '''
124 DOWN_GIF = '''\
125 R0lGODlhCwAMALMAANnZ2QDMmQCZZgBmZgAAAAAzM////////wAAAAAAAAAAAAAAAAAAAAAAAAAA
126 AAAAACH5BAEAAAAALAAAAAALAAwAAAQmEIQxgLVUCsppsVPngVtXEFfIfWk5nBe4xuSL0tKLy/cu
127 7JffJQIAOw==
128 '''
129 LEFT_GIF='''\
130 R0lGODlhDAALAKIAANnZ2QDMmQCZZgBmZgAAAAAzM////////yH5BAEAAAAALAAAAAAMAAsAAAM4
131 CLocgaCrESiDoBshOAoAgBEyMzgAEIGCowsiOLoLgEBVOLoIqlSFo4OgC1RYM4Ogq1RYg6DLVJgA
132 Ow==
133 '''
134 RIGHT_GIF='''\
135 R0lGODlhDAALAKIAANnZ2QDMmQBmZgCZZgAzMwAAAP///////yH5BAEAAAAALAAAAAAMAAsAAAM5
136 GIGgyzIYgaCrIigTgaALIigyEQiqKLoTgaAoujuDgKJLVAgqIoJEBQAIIkKEhaArRFgIukqFoMsJ
137 ADs=
138 '''
139
140
141
142
143
144 from epydoc import log
145 from epydoc.util import wordwrap
147 _STAGES = [40, 7, 1, 3, 30, 1, 2, 100]
148
150 self._progress = progress
151 self._cancel = cancel
152 self.clear()
153
155 self._messages = []
156 self._n = 0
157 self._stage = 0
158 self._message_blocks = []
159
160 - def log(self, level, message):
161 message = wordwrap(str(message)).rstrip() + '\n'
162 if self._message_blocks:
163 self._message_blocks[-1][-1].append( (level, message) )
164 else:
165 self._messages.append( (level, message) )
166
168 self._message_blocks.append( (header, []) )
169
171 header, messages = self._message_blocks.pop()
172 if messages:
173 self._messages.append( ('uline', ' '*75+'\n') )
174 self.log('header', header)
175 self._messages += messages
176 self._messages.append( ('uline', ' '*75+'\n') )
177
179 self.log(log.INFO, header)
180 self._stage += 1
181
183 pass
184
185 - def progress(self, percent, message=''):
186 if self._cancel[0]: exit_thread()
187 i = self._stage - 1
188 p = ((sum(self._STAGES[:i]) + percent*self._STAGES[i]) /
189 float(sum(self._STAGES)))
190 self._progress[0] = p
191
193 if self._n >= len(self._messages):
194 return None, None
195 else:
196 self._n += 1
197 return self._messages[self._n-1]
198
199
200
201
202
204 """
205 Create the documentation for C{modules}, using the options
206 specified by C{options}. C{document} is designed to be started in
207 its own thread by L{EpydocGUI._go}.
208
209 @param options: The options to use for generating documentation.
210 This includes keyword options that can be given to
211 L{html.HTMLFormatter}, as well as the option C{outdir}, which
212 controls where the output is written to.
213 @type options: C{dictionary}
214 """
215 from epydoc.docwriter.html import HTMLWriter
216 from epydoc.docbuilder import build_doc_index
217 import epydoc.docstringparser
218
219
220 docformat = options.get('docformat', 'epytext')
221 epydoc.docstringparser.DEFAULT_DOCFORMAT = docformat
222
223 try:
224 parse = options['introspect_or_parse'] in ('parse', 'both')
225 introspect = options['introspect_or_parse'] in ('introspect', 'both')
226 docindex = build_doc_index(options['modules'], parse, introspect)
227 html_writer = HTMLWriter(docindex, **options)
228 log.start_progress('Writing HTML docs to %r' % options['target'])
229 html_writer.write(options['target'])
230 log.end_progress()
231
232
233 log.warning('Finished!')
234 done[0] = 'done'
235
236 except SystemExit:
237
238 log.error('Cancelled!')
239 done[0] ='cancel'
240 raise
241 except Exception, e:
242
243 log.error('Internal error: %s' % e)
244 done[0] ='cancel'
245 raise
246 except:
247
248 log.error('Internal error!')
249 done[0] ='cancel'
250 raise
251
252
253
254
255
257 """
258 A graphical user interace to epydoc.
259 """
261 self._afterid = 0
262 self._progress = [None]
263 self._cancel = [0]
264 self._filename = None
265 self._init_dir = None
266
267
268
269
270
271
272
273 self._old_modules = sys.modules.keys()
274
275
276 self._root = Tk()
277 self._root['background']=BG_COLOR
278 self._root.bind('<Control-q>', self.destroy)
279 self._root.bind('<Alt-q>', self.destroy)
280 self._root.bind('<Alt-x>', self.destroy)
281 self._root.bind('<Control-x>', self.destroy)
282
283 self._root.title('Epydoc')
284 self._rootframe = Frame(self._root, background=BG_COLOR,
285 border=2, relief='raised')
286 self._rootframe.pack(expand=1, fill='both', padx=2, pady=2)
287
288
289
290 leftframe = Frame(self._rootframe, background=BG_COLOR)
291 leftframe.pack(expand=1, fill='both', side='left')
292 optsframe = Frame(self._rootframe, background=BG_COLOR)
293 mainframe = Frame(leftframe, background=BG_COLOR)
294 mainframe.pack(expand=1, fill='both', side='top')
295 ctrlframe = Frame(mainframe, background=BG_COLOR)
296 ctrlframe.pack(side="bottom", fill='x', expand=0)
297 msgsframe = Frame(leftframe, background=BG_COLOR)
298
299 self._optsframe = optsframe
300 self._msgsframe = msgsframe
301
302
303 self._init_menubar()
304 self._init_progress_bar(mainframe)
305 self._init_module_list(mainframe)
306 self._init_options(optsframe, ctrlframe)
307 self._init_messages(msgsframe, ctrlframe)
308 self._init_bindings()
309
310
311 self._logger = GUILogger(self._progress, self._cancel)
312 log.register_logger(self._logger)
313
314
315 self._messages_toggle()
316
317
318
319
321 menubar = Menu(self._root, borderwidth=2,
322 background=BG_COLOR,
323 activebackground=BG_COLOR)
324 filemenu = Menu(menubar, tearoff=0)
325 filemenu.add_command(label='New Project', underline=0,
326 command=self._new,
327 accelerator='Ctrl-n')
328 filemenu.add_command(label='Open Project', underline=0,
329 command=self._open,
330 accelerator='Ctrl-o')
331 filemenu.add_command(label='Save Project', underline=0,
332 command=self._save,
333 accelerator='Ctrl-s')
334 filemenu.add_command(label='Save As..', underline=5,
335 command=self._saveas,
336 accelerator='Ctrl-a')
337 filemenu.add_separator()
338 filemenu.add_command(label='Exit', underline=1,
339 command=self.destroy,
340 accelerator='Ctrl-x')
341 menubar.add_cascade(label='File', underline=0, menu=filemenu)
342 gomenu = Menu(menubar, tearoff=0)
343 gomenu.add_command(label='Run Epydoc', command=self._open,
344 underline=0, accelerator='Alt-g')
345 menubar.add_cascade(label='Run', menu=gomenu, underline=0)
346 self._root.config(menu=menubar)
347
349 mframe1 = Frame(mainframe, relief='groove', border=2,
350 background=BG_COLOR)
351 mframe1.pack(side="top", fill='both', expand=1, padx=4, pady=3)
352 l = Label(mframe1, text="Modules to document:",
353 justify='left', **COLOR_CONFIG)
354 l.pack(side='top', fill='none', anchor='nw', expand=0)
355 mframe2 = Frame(mframe1, background=BG_COLOR)
356 mframe2.pack(side="top", fill='both', expand=1)
357 mframe3 = Frame(mframe1, background=BG_COLOR)
358 mframe3.pack(side="bottom", fill='x', expand=0)
359 self._module_list = Listbox(mframe2, width=80, height=10,
360 selectmode='multiple',
361 **LISTBOX_CONFIG)
362 self._module_list.pack(side="left", fill='both', expand=1)
363 sb = Scrollbar(mframe2, orient='vertical',**SB_CONFIG)
364 sb['command']=self._module_list.yview
365 sb.pack(side='right', fill='y')
366 self._module_list.config(yscrollcommand=sb.set)
367 Label(mframe3, text="Add:", **COLOR_CONFIG).pack(side='left')
368 self._module_entry = Entry(mframe3, **ENTRY_CONFIG)
369 self._module_entry.pack(side='left', fill='x', expand=1)
370 self._module_entry.bind('<Return>', self._entry_module)
371 self._module_browse = Button(mframe3, text="Browse",
372 command=self._browse_module,
373 **BUTTON_CONFIG)
374 self._module_browse.pack(side='right', expand=0, padx=2)
375
377 pframe1 = Frame(mainframe, background=BG_COLOR)
378 pframe1.pack(side="bottom", fill='x', expand=0)
379 self._go_button = Button(pframe1, width=4, text='Start',
380 underline=0, command=self._go,
381 **BUTTON_CONFIG)
382 self._go_button.pack(side='left', padx=4)
383 pframe2 = Frame(pframe1, relief='groove', border=2,
384 background=BG_COLOR)
385 pframe2.pack(side="top", fill='x', expand=1, padx=4, pady=3)
386 Label(pframe2, text='Progress:', **COLOR_CONFIG).pack(side='left')
387 H = self._H = PROGRESS_HEIGHT
388 W = self._W = PROGRESS_WIDTH
389 c = self._canvas = Canvas(pframe2, height=H+DH, width=W+DW,
390 background=PROGRESS_BG, border=0,
391 selectborderwidth=0, relief='sunken',
392 insertwidth=0, insertborderwidth=0,
393 highlightbackground=BG_COLOR)
394 self._canvas.pack(side='left', fill='x', expand=1, padx=4)
395 self._r2 = c.create_rectangle(0,0,0,0, outline=PROGRESS_COLOR2)
396 self._r3 = c.create_rectangle(0,0,0,0, outline=PROGRESS_COLOR3)
397 self._r1 = c.create_rectangle(0,0,0,0, fill=PROGRESS_COLOR1,
398 outline='')
399 self._canvas.bind('<Configure>', self._configure)
400
402 self._downImage = PhotoImage(master=self._root, data=DOWN_GIF)
403 self._upImage = PhotoImage(master=self._root, data=UP_GIF)
404
405
406 b1 = Button(ctrlframe, text="Messages", justify='center',
407 command=self._messages_toggle, underline=0,
408 highlightthickness=0, activebackground=BG_COLOR,
409 border=0, relief='flat', padx=2, pady=0, **COLOR_CONFIG)
410 b2 = Button(ctrlframe, image=self._downImage, relief='flat',
411 border=0, command=self._messages_toggle,
412 activebackground=BG_COLOR, **COLOR_CONFIG)
413 self._message_button = b2
414 self._messages_visible = 0
415 b2.pack(side="left")
416 b1.pack(side="left")
417
418 f = Frame(msgsframe, background=BG_COLOR)
419 f.pack(side='top', expand=1, fill='both')
420 messages = Text(f, width=80, height=10, **ENTRY_CONFIG)
421 messages['state'] = 'disabled'
422 messages.pack(fill='both', expand=1, side='left')
423 self._messages = messages
424
425
426 sb = Scrollbar(f, orient='vertical', **SB_CONFIG)
427 sb.pack(fill='y', side='right')
428 sb['command'] = messages.yview
429 messages['yscrollcommand'] = sb.set
430
431
432 messages.tag_config('error', foreground=ERROR_COLOR)
433 messages.tag_config('warning', foreground=WARNING_COLOR)
434 messages.tag_config('guierror', foreground=GUIERROR_COLOR)
435 messages.tag_config('message', foreground=MESSAGE_COLOR)
436 messages.tag_config('header', foreground=HEADER_COLOR)
437 messages.tag_config('uline', underline=1)
438
439
440 self._in_header = 0
441 self._last_tag = 'error'
442
443
444 buttons = Frame(msgsframe, background=BG_COLOR)
445 buttons.pack(side='bottom', fill='x')
446 self._show_errors = IntVar(self._root)
447 self._show_errors.set(1)
448 self._show_warnings = IntVar(self._root)
449 self._show_warnings.set(1)
450 self._show_messages = IntVar(self._root)
451 self._show_messages.set(0)
452 Checkbutton(buttons, text='Show Messages', var=self._show_messages,
453 command=self._update_msg_tags,
454 **SHOWMSG_CONFIG).pack(side='left')
455 Checkbutton(buttons, text='Show Warnings', var=self._show_warnings,
456 command=self._update_msg_tags,
457 **SHOWWRN_CONFIG).pack(side='left')
458 Checkbutton(buttons, text='Show Errors', var=self._show_errors,
459 command=self._update_msg_tags,
460 **SHOWERR_CONFIG).pack(side='left')
461 self._update_msg_tags()
462
464 elide_errors = not self._show_errors.get()
465 elide_warnings = not self._show_warnings.get()
466 elide_messages = not self._show_messages.get()
467 elide_headers = elide_errors and elide_warnings
468 self._messages.tag_config('error', elide=elide_errors)
469 self._messages.tag_config('guierror', elide=elide_errors)
470 self._messages.tag_config('warning', elide=elide_warnings)
471 self._messages.tag_config('message', elide=elide_messages)
472 self._messages.tag_config('header', elide=elide_headers)
473
475 self._leftImage=PhotoImage(master=self._root, data=LEFT_GIF)
476 self._rightImage=PhotoImage(master=self._root, data=RIGHT_GIF)
477
478
479 b1 = Button(ctrlframe, text="Options", justify='center',
480 border=0, relief='flat',
481 command=self._options_toggle, padx=2,
482 underline=0, pady=0, highlightthickness=0,
483 activebackground=BG_COLOR, **COLOR_CONFIG)
484 b2 = Button(ctrlframe, image=self._rightImage, relief='flat',
485 border=0, command=self._options_toggle,
486 activebackground=BG_COLOR, **COLOR_CONFIG)
487 self._option_button = b2
488 self._options_visible = 0
489 b2.pack(side="right")
490 b1.pack(side="right")
491
492 oframe2 = Frame(optsframe, relief='groove', border=2,
493 background=BG_COLOR)
494 oframe2.pack(side="right", fill='both',
495 expand=0, padx=4, pady=3, ipadx=4)
496
497 Label(oframe2, text="Project Options", font='helvetica -16',
498 **COLOR_CONFIG).pack(anchor='w')
499 oframe3 = Frame(oframe2, background=BG_COLOR)
500 oframe3.pack(fill='x')
501 oframe4 = Frame(oframe2, background=BG_COLOR)
502 oframe4.pack(fill='x')
503 oframe7 = Frame(oframe2, background=BG_COLOR)
504 oframe7.pack(fill='x')
505 div = Frame(oframe2, background=BG_COLOR, border=1, relief='sunk')
506 div.pack(ipady=1, fill='x', padx=4, pady=2)
507
508 Label(oframe2, text="Help File", font='helvetica -16',
509 **COLOR_CONFIG).pack(anchor='w')
510 oframe5 = Frame(oframe2, background=BG_COLOR)
511 oframe5.pack(fill='x')
512 div = Frame(oframe2, background=BG_COLOR, border=1, relief='sunk')
513 div.pack(ipady=1, fill='x', padx=4, pady=2)
514
515 Label(oframe2, text="CSS Stylesheet", font='helvetica -16',
516 **COLOR_CONFIG).pack(anchor='w')
517 oframe6 = Frame(oframe2, background=BG_COLOR)
518 oframe6.pack(fill='x')
519
520
521
522 row = 0
523 l = Label(oframe3, text="Project Name:", **COLOR_CONFIG)
524 l.grid(row=row, column=0, sticky='e')
525 self._name_entry = Entry(oframe3, **ENTRY_CONFIG)
526 self._name_entry.grid(row=row, column=1, sticky='ew', columnspan=3)
527
528
529 row += 1
530 l = Label(oframe3, text="Project URL:", **COLOR_CONFIG)
531 l.grid(row=row, column=0, sticky='e')
532 self._url_entry = Entry(oframe3, **ENTRY_CONFIG)
533 self._url_entry.grid(row=row, column=1, sticky='ew', columnspan=3)
534
535
536 row += 1
537 l = Label(oframe3, text="Output Directory:", **COLOR_CONFIG)
538 l.grid(row=row, column=0, sticky='e')
539 self._out_entry = Entry(oframe3, **ENTRY_CONFIG)
540 self._out_entry.grid(row=row, column=1, sticky='ew', columnspan=2)
541 self._out_browse = Button(oframe3, text="Browse",
542 command=self._browse_out,
543 **BUTTON_CONFIG)
544 self._out_browse.grid(row=row, column=3, sticky='ew', padx=2)
545
546
547
548 row = 0
549 self._frames_var = IntVar(self._root)
550 self._frames_var.set(1)
551 l = Label(oframe4, text="Generate a frame-based table of contents",
552 **COLOR_CONFIG)
553 l.grid(row=row, column=1, sticky='w')
554 cb = Checkbutton(oframe4, var=self._frames_var, **CBUTTON_CONFIG)
555 cb.grid(row=row, column=0, sticky='e')
556
557
558 row += 1
559 self._private_var = IntVar(self._root)
560 self._private_var.set(1)
561 l = Label(oframe4, text="Generate documentation for private objects",
562 **COLOR_CONFIG)
563 l.grid(row=row, column=1, sticky='w')
564 cb = Checkbutton(oframe4, var=self._private_var, **CBUTTON_CONFIG)
565 cb.grid(row=row, column=0, sticky='e')
566
567
568 row += 1
569 self._imports_var = IntVar(self._root)
570 self._imports_var.set(0)
571 l = Label(oframe4, text="List imported classes and functions",
572 **COLOR_CONFIG)
573 l.grid(row=row, column=1, sticky='w')
574 cb = Checkbutton(oframe4, var=self._imports_var, **CBUTTON_CONFIG)
575 cb.grid(row=row, column=0, sticky='e')
576
577
578
579 row += 1
580 l = Label(oframe7, text="Default Docformat:", **COLOR_CONFIG)
581 l.grid(row=row, column=0, sticky='e')
582 df_var = self._docformat_var = StringVar(self._root)
583 self._docformat_var.set('epytext')
584 b = Radiobutton(oframe7, var=df_var, text='Epytext',
585 value='epytext', **CBUTTON_CONFIG)
586 b.grid(row=row, column=1, sticky='w')
587 b = Radiobutton(oframe7, var=df_var, text='ReStructuredText',
588 value='restructuredtext', **CBUTTON_CONFIG)
589 b.grid(row=row, column=2, columnspan=2, sticky='w')
590 row += 1
591 b = Radiobutton(oframe7, var=df_var, text='Plaintext',
592 value='plaintext', **CBUTTON_CONFIG)
593 b.grid(row=row, column=1, sticky='w')
594 b = Radiobutton(oframe7, var=df_var, text='Javadoc',
595 value='javadoc', **CBUTTON_CONFIG)
596 b.grid(row=row, column=2, columnspan=2, sticky='w')
597 row += 1
598
599
600 Frame(oframe7, background=BG_COLOR).grid(row=row, column=1, pady=3)
601 row += 1
602
603
604 l = Label(oframe7, text="Inheritance Style:", **COLOR_CONFIG)
605 l.grid(row=row, column=0, sticky='e')
606 inh_var = self._inheritance_var = StringVar(self._root)
607 self._inheritance_var.set('grouped')
608 b = Radiobutton(oframe7, var=inh_var, text='Grouped',
609 value='grouped', **CBUTTON_CONFIG)
610 b.grid(row=row, column=1, sticky='w')
611 b = Radiobutton(oframe7, var=inh_var, text='Listed',
612 value='listed', **CBUTTON_CONFIG)
613 b.grid(row=row, column=2, sticky='w')
614 b = Radiobutton(oframe7, var=inh_var, text='Included',
615 value='included', **CBUTTON_CONFIG)
616 b.grid(row=row, column=3, sticky='w')
617 row += 1
618
619
620 Frame(oframe7, background=BG_COLOR).grid(row=row, column=1, pady=3)
621 row += 1
622
623
624 l = Label(oframe7, text="Get docs from:", **COLOR_CONFIG)
625 l.grid(row=row, column=0, sticky='e')
626 iop_var = self._introspect_or_parse_var = StringVar(self._root)
627 self._introspect_or_parse_var.set('both')
628 b = Radiobutton(oframe7, var=iop_var, text='Parsing',
629 value='parse', **CBUTTON_CONFIG)
630 b.grid(row=row, column=1, sticky='w')
631 b = Radiobutton(oframe7, var=iop_var, text='Introspecting',
632 value='introspect', **CBUTTON_CONFIG)
633 b.grid(row=row, column=2, sticky='w')
634 b = Radiobutton(oframe7, var=iop_var, text='Both',
635 value='both', **CBUTTON_CONFIG)
636 b.grid(row=row, column=3, sticky='w')
637 row += 1
638
639
640
641 row = 0
642 self._help_var = StringVar(self._root)
643 self._help_var.set('default')
644 b = Radiobutton(oframe5, var=self._help_var,
645 text='Default',
646 value='default', **CBUTTON_CONFIG)
647 b.grid(row=row, column=1, sticky='w')
648 row += 1
649 b = Radiobutton(oframe5, var=self._help_var,
650 text='Select File',
651 value='-other-', **CBUTTON_CONFIG)
652 b.grid(row=row, column=1, sticky='w')
653 self._help_entry = Entry(oframe5, **ENTRY_CONFIG)
654 self._help_entry.grid(row=row, column=2, sticky='ew')
655 self._help_browse = Button(oframe5, text='Browse',
656 command=self._browse_help,
657 **BUTTON_CONFIG)
658 self._help_browse.grid(row=row, column=3, sticky='ew', padx=2)
659
660 from epydoc.docwriter.html_css import STYLESHEETS
661 items = STYLESHEETS.items()
663 if css1[0] == 'default': return -1
664 elif css2[0] == 'default': return 1
665 else: return cmp(css1[0], css2[0])
666 items.sort(_css_sort)
667
668
669
670
671 row = 0
672
673
674
675
676 row += 1
677 css_var = self._css_var = StringVar(self._root)
678 css_var.set('default')
679
680
681 for (name, (sheet, descr)) in items:
682 b = Radiobutton(oframe6, var=css_var, value=name, **CBUTTON_CONFIG)
683 b.grid(row=row, column=0, sticky='e')
684
685
686
687 l = Label(oframe6, text=descr, **COLOR_CONFIG)
688 l.grid(row=row, column=1, sticky='w')
689 row += 1
690 b = Radiobutton(oframe6, var=css_var, value='-other-',
691 **CBUTTON_CONFIG)
692 b.grid(row=row, column=0, sticky='e')
693
694
695
696
697
698 self._css_entry = Entry(oframe6, **ENTRY_CONFIG)
699 self._css_entry.grid(row=row, column=1, sticky='ew')
700 self._css_browse = Button(oframe6, text="Browse",
701 command=self._browse_css,
702 **BUTTON_CONFIG)
703 self._css_browse.grid(row=row, column=2, sticky='ew', padx=2)
704
706 self._root.bind('<Delete>', self._delete_module)
707 self._root.bind('<Alt-o>', self._options_toggle)
708 self._root.bind('<Alt-m>', self._messages_toggle)
709 self._root.bind('<F5>', self._go)
710 self._root.bind('<Alt-s>', self._go)
711
712 self._root.bind('<Control-n>', self._new)
713 self._root.bind('<Control-o>', self._open)
714 self._root.bind('<Control-s>', self._save)
715 self._root.bind('<Control-a>', self._saveas)
716
718 if self._options_visible:
719 self._optsframe.forget()
720 self._option_button['image'] = self._rightImage
721 self._options_visible = 0
722 else:
723 self._optsframe.pack(fill='both', side='right')
724 self._option_button['image'] = self._leftImage
725 self._options_visible = 1
726
728 if self._messages_visible:
729 self._msgsframe.forget()
730 self._message_button['image'] = self._rightImage
731 self._messages_visible = 0
732 else:
733 self._msgsframe.pack(fill='both', side='bottom', expand=1)
734 self._message_button['image'] = self._leftImage
735 self._messages_visible = 1
736
739
741 selection = self._module_list.curselection()
742 if len(selection) != 1: return
743 self._module_list.delete(selection[0])
744
745 - def _entry_module(self, *e):
746 modules = [self._module_entry.get()]
747 if glob.has_magic(modules[0]):
748 modules = glob.glob(modules[0])
749 for name in modules:
750 self.add_module(name, check=1)
751 self._module_entry.delete(0, 'end')
752
754 title = 'Select a module for documentation'
755 ftypes = [('Python module', '.py'),
756 ('Python extension', '.so'),
757 ('All files', '*')]
758 filename = askopenfilename(filetypes=ftypes, title=title,
759 defaultextension='.py',
760 initialdir=self._init_dir)
761 if not filename: return
762 self._init_dir = os.path.dirname(filename)
763 self.add_module(filename, check=1)
764
766 title = 'Select a CSS stylesheet'
767 ftypes = [('CSS Stylesheet', '.css'), ('All files', '*')]
768 filename = askopenfilename(filetypes=ftypes, title=title,
769 defaultextension='.css')
770 if not filename: return
771 self._css_entry.delete(0, 'end')
772 self._css_entry.insert(0, filename)
773
775 title = 'Select a help file'
776 self._help_var.set('-other-')
777 ftypes = [('HTML file', '.html'), ('All files', '*')]
778 filename = askopenfilename(filetypes=ftypes, title=title,
779 defaultextension='.html')
780 if not filename: return
781 self._help_entry.delete(0, 'end')
782 self._help_entry.insert(0, filename)
783
785 ftypes = [('All files', '*')]
786 title = 'Choose the output directory'
787 if askdirectory is not None:
788 filename = askdirectory(mustexist=0, title=title)
789 if not filename: return
790 else:
791
792 filename = asksaveasfilename(filetypes=ftypes, title=title,
793 initialfile='--this directory--')
794 if not filename: return
795 (f1, f2) = os.path.split(filename)
796 if f2 == '--this directory--': filename = f1
797 self._out_entry.delete(0, 'end')
798 self._out_entry.insert(0, filename)
799
801 if self._root is None: return
802
803
804 for m in sys.modules.keys():
805 if m not in self._old_modules: del sys.modules[m]
806 self._root.destroy()
807 self._root = None
808
837
838 - def mainloop(self, *args, **kwargs):
839 self._root.mainloop(*args, **kwargs)
840
842 options = {}
843 options['modules'] = self._module_list.get(0, 'end')
844 options['prj_name'] = self._name_entry.get() or ''
845 options['prj_url'] = self._url_entry.get() or None
846 options['docformat'] = self._docformat_var.get()
847 options['inheritance'] = self._inheritance_var.get()
848 options['introspect_or_parse'] = self._introspect_or_parse_var.get()
849 options['target'] = self._out_entry.get() or 'html'
850 options['frames'] = self._frames_var.get()
851 options['private'] = self._private_var.get()
852 options['show_imports'] = self._imports_var.get()
853 if self._help_var.get() == '-other-':
854 options['help'] = self._help_entry.get() or None
855 else:
856 options['help'] = None
857 if self._css_var.get() == '-other-':
858 options['css'] = self._css_entry.get() or 'default'
859 else:
860 options['css'] = self._css_var.get() or 'default'
861
862
863
864
865 return options
866
868 if len(self._module_list.get(0,'end')) == 0:
869 self._root.bell()
870 return
871
872 if self._progress[0] != None:
873 self._cancel[0] = 1
874 return
875
876
877 opts = self._getopts()
878 self._progress[0] = 0.0
879 self._cancel[0] = 0
880 args = (opts, self._cancel, self._progress)
881
882
883 self._messages['state'] = 'normal'
884 self._messages.delete('0.0', 'end')
885 self._messages['state'] = 'disabled'
886 self._logger.clear()
887
888
889
890 for m in sys.modules.keys():
891 if m not in self._old_modules:
892 del sys.modules[m]
893
894
895
896
897 start_new_thread(document, args)
898
899
900 self._go_button['text'] = 'Stop'
901 self._afterid += 1
902 dt = 300
903 self._update(dt, self._afterid)
904
906 while 1:
907 level, data = self._logger.read()
908 if data is None: break
909 self._messages['state'] = 'normal'
910 if level == 'header':
911 self._messages.insert('end', data, 'header')
912 elif level == 'uline':
913 self._messages.insert('end', data, 'uline header')
914 elif level >= log.ERROR:
915 data= data.rstrip()+'\n\n'
916 self._messages.insert('end', data, 'guierror')
917 elif level >= log.DOCSTRING_WARNING:
918 data= data.rstrip()+'\n\n'
919 self._messages.insert('end', data, 'warning')
920 elif log >= log.INFO:
921 data= data.rstrip()+'\n\n'
922 self._messages.insert('end', data, 'message')
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945 self._messages['state'] = 'disabled'
946 self._messages.yview('end')
947
949 if self._root is None: return
950 if self._progress[0] is None: return
951 if id != self._afterid: return
952
953
954 self._update_messages()
955
956
957 if self._progress[0] == 'done': p = self._W + DX
958 elif self._progress[0] == 'cancel': p = -5
959 else: p = DX + self._W * self._progress[0]
960 self._canvas.coords(self._r1, DX+1, DY+1, p, self._H+1)
961 self._canvas.coords(self._r2, DX, DY, p-1, self._H)
962 self._canvas.coords(self._r3, DX+1, DY+1, p, self._H+1)
963
964
965 if self._progress[0] in ('done', 'cancel'):
966 if self._progress[0] == 'cancel': self._root.bell()
967 self._go_button['text'] = 'Start'
968 self._progress[0] = None
969 return
970
971 self._root.after(dt, self._update, dt, id)
972
973 - def _new(self, *e):
974 self._module_list.delete(0, 'end')
975 self._name_entry.delete(0, 'end')
976 self._url_entry.delete(0, 'end')
977 self._docformat_var.set('epytext')
978 self._inheritance_var.set('grouped')
979 self._introspect_or_parse_var.set('both')
980 self._out_entry.delete(0, 'end')
981 self._module_entry.delete(0, 'end')
982 self._css_entry.delete(0, 'end')
983 self._help_entry.delete(0, 'end')
984 self._frames_var.set(1)
985 self._private_var.set(1)
986 self._imports_var.set(0)
987 self._css_var.set('default')
988
989 self._help_var.set('default')
990 self._filename = None
991 self._init_dir = None
992
994 title = 'Open project'
995 ftypes = [('Project file', '.prj'),
996 ('All files', '*')]
997 filename = askopenfilename(filetypes=ftypes, title=title,
998 defaultextension='.css')
999 if not filename: return
1000 self.open(filename)
1001
1002 - def open(self, prjfile):
1003 from epydoc.docwriter.html_css import STYLESHEETS
1004 self._filename = prjfile
1005 try:
1006 opts = load(open(prjfile, 'r'))
1007
1008 modnames = list(opts.get('modules', []))
1009 modnames.sort()
1010 self._module_list.delete(0, 'end')
1011 for name in modnames:
1012 self.add_module(name)
1013 self._module_entry.delete(0, 'end')
1014
1015 self._name_entry.delete(0, 'end')
1016 if opts.get('prj_name'):
1017 self._name_entry.insert(0, opts['prj_name'])
1018
1019 self._url_entry.delete(0, 'end')
1020 if opts.get('prj_url'):
1021 self._url_entry.insert(0, opts['prj_url'])
1022
1023 self._docformat_var.set(opts.get('docformat', 'epytext'))
1024 self._inheritance_var.set(opts.get('inheritance', 'grouped'))
1025 self._introspect_or_parse_var.set(
1026 opts.get('introspect_or_parse', 'both'))
1027
1028 self._help_entry.delete(0, 'end')
1029 if opts.get('help') is None:
1030 self._help_var.set('default')
1031 else:
1032 self._help_var.set('-other-')
1033 self._help_entry.insert(0, opts.get('help'))
1034
1035 self._out_entry.delete(0, 'end')
1036 self._out_entry.insert(0, opts.get('outdir', 'html'))
1037
1038 self._frames_var.set(opts.get('frames', 1))
1039 self._private_var.set(opts.get('private', 1))
1040 self._imports_var.set(opts.get('show_imports', 0))
1041
1042 self._css_entry.delete(0, 'end')
1043 if opts.get('css', 'default') in STYLESHEETS.keys():
1044 self._css_var.set(opts.get('css', 'default'))
1045 else:
1046 self._css_var.set('-other-')
1047 self._css_entry.insert(0, opts.get('css', 'default'))
1048
1049
1050
1051
1052
1053
1054
1055 except Exception, e:
1056 log.error('Error opening %s: %s' % (prjfile, e))
1057 self._root.bell()
1058
1060 if self._filename is None: return self._saveas()
1061 try:
1062 opts = self._getopts()
1063 dump(opts, open(self._filename, 'w'))
1064 except Exception, e:
1065 if self._filename is None:
1066 log.error('Error saving: %s' % e)
1067 else:
1068 log.error('Error saving %s: %s' % (self._filename, e))
1069 self._root.bell()
1070
1072 title = 'Save project as'
1073 ftypes = [('Project file', '.prj'), ('All files', '*')]
1074 filename = asksaveasfilename(filetypes=ftypes, title=title,
1075 defaultextension='.prj')
1076 if not filename: return
1077 self._filename = filename
1078 self._save()
1079
1081 """
1082 Display the version information, and exit.
1083 @rtype: C{None}
1084 """
1085 import epydoc
1086 print "Epydoc version %s" % epydoc.__version__
1087 sys.exit(0)
1088
1089
1090
1091
1093 print
1094 print 'Usage: epydocgui [OPTIONS] [FILE.prj | MODULES...]'
1095 print
1096 print ' FILE.prj An epydoc GUI project file.'
1097 print ' MODULES... A list of Python modules to document.'
1098 print ' -V, --version Print the version of epydoc.'
1099 print ' -h, -?, --help, --usage Display this usage message'
1100 print ' --debug Do not suppress error messages'
1101 print
1102 sys.exit(0)
1103
1105 s = '%s; run "%s -h" for usage' % (s, os.path.basename(sys.argv[0]))
1106 if len(s) > 80:
1107 i = s.rfind(' ', 0, 80)
1108 if i>0: s = s[:i]+'\n'+s[i+1:]
1109 print >>sys.stderr, s
1110 sys.exit(1)
1111
1113 global DEBUG
1114 sys.stderr = sys.__stderr__
1115 projects = []
1116 modules = []
1117 for arg in sys.argv[1:]:
1118 if arg[0] == '-':
1119 arg = arg.lower()
1120 if arg in ('-h', '--help', '-?', '--usage'): _usage()
1121 elif arg in ('-V', '--version'): _version()
1122 elif arg in ('--debug',): DEBUG = 1
1123 else:
1124 _error('Unknown parameter %r' % arg)
1125 elif arg[-4:] == '.prj': projects.append(arg)
1126 else: modules.append(arg)
1127
1128 if len(projects) > 1:
1129 _error('Too many projects')
1130 if len(projects) == 1:
1131 if len(modules) > 0:
1132 _error('You must specify either a project or a list of modules')
1133 if not os.path.exists(projects[0]):
1134 _error('Cannot open project file %s' % projects[0])
1135 gui = EpydocGUI()
1136 gui.open(projects[0])
1137 gui.mainloop()
1138 else:
1139 gui = EpydocGUI()
1140 for module in modules: gui.add_module(module, check=1)
1141 gui.mainloop()
1142
1143 if __name__ == '__main__': gui()
1144