import Tkinter, Pmw, tkFileDialog, base from tkMessageBox import showerror, showinfo, askyesno from tkSimpleDialog import askstring import os, string, sys, project, version root = None directory_choices=['.'] directory_choices_map = { '.':'Current directory' } class PyfortpGui: "Create and Edit .pfp files." balloon_text = \ """pyfort -e creates a project file with a .pfp (PyFortProject) extension. This is a text file which you can edit with Pyfort or with a text editor. You can use the project file to compile and install a Pyfort extension. Use the item on the Help menu to turn this balloon help on and off. """ command_summary = """\ Pyfort Project File Editor The notebook has a page for each Pyfort module file. You can switch from one page to the other by choosing a tab. Buttons at the bottom: New PYF -- Add a new Pyfort module to the project Remove PYF -- Remove a Pyfort module from the project Check -- Performs basic checks on the project. Finish -- Save, exit editor. Cancel -- Exit Pyfort without saving any changes. """ def __init__(self, p): self.p = p # the project object self.dialog = base.MyDialog(command=self.execute, buttons=('New PYF', 'Remove PYF', 'Check', 'Finish', 'Cancel') ) self.dialog.component('buttonbox').interior().configure(bg='light green') base.balloon.bind(self.dialog.component('buttonbox'), PyfortpGui.command_summary ) base.balloon.bind(self.dialog.interior(), PyfortpGui.balloon_text) self.dialog.withdraw() # create the menus main_menu = Pmw.MenuBar(self.dialog.interior(), hull_relief = 'raised', hull_borderwidth = 2, balloon=base.balloon ) main_menu.pack(side='top', fill='both') self.create_filemenu(main_menu) self.create_help_menu(main_menu) self.create_forms(self.dialog.interior()) self._project2gui() self.dialog.geometry("+50+50") self.dialog.deiconify() self.dialog.dialog.lift() self.notebook.pack(expand=1, fill='both') if not self.p.pyfs: self.pyf_newfile() else: self.notebook.selectpage(0) self.notebook.setnaturalsize() def create_forms (self, parent): self.mainframe = Tkinter.Frame(parent) self.mainframe.pack(fill='both', expand=1) self.notebook = Pmw.NoteBook(self.mainframe, hull_borderwidth=3, hull_highlightthickness=2, tabpos='n' ) self.notebook.component('hull').configure(bg='light blue') base.balloon.bind(self.notebook, """First create your Pyfort file and then add it to the project using the New PYF button. The name of the Pyfort file, less its ".pyf" extension, will be the name used for the Fortran extension module it generates. """) def pyf_newfile (self): self._gui2project() here = os.getcwd() dialog = tkFileDialog.Open(master=self.dialog.interior(), filetypes=[('Pyfort input file', '*.pyf')], title="Choose Pyfort Input File") filename = dialog.show(initialdir=here) if filename: if filename in [x.filename for x in self.p.pyfs]: showerror('Duplicate', 'That file is already in the project.') return filename = filename.replace(here+os.sep, '') x = project.pyf(filename) self.p.pyfs.append(x) self._project2gui() self.notebook.selectpage(x.name) self.notebook.setnaturalsize() def pyf_remove(self): self._gui2project () # save what is there page = self.notebook.getcurselection() x = askyesno('Remove PYF?', 'Remove ' + page + "?") if x: self.p.pyfs = [y for y in self.p.pyfs if y.name != page] self._project2gui() def valid(self): self._gui2project() msg = self.p.validate() if msg: showerror('Project definition error', msg) else: showinfo('OK', 'Project checked ok.') def _project2gui (self): "Get the state from the project to the Gui" self.set_title(self.p.filename) for x in self.notebook.pagenames(): self.notebook.delete(x) self.editors = {} for x in self.p.pyfs: page = self.notebook.insert(x.name, 0, tab_text=x.name) editor = PyfEditor(page) editor.put(x) self.editors[x.name] = editor def set_title(self, name): title = "Pyfort Project Editor -- %s" % name self.dialog.title(title) def create_filemenu(self, main_menu): main_menu.addmenu('File', 'Working with the PFP file', side='left', tearoff = 0) main_menu.addmenuitem('File', 'command', 'Save: save current values to file', label = "Save", command = self.save ) main_menu.addmenuitem('File', 'command', 'Exit: exit the editor', label = "Exit", command = self.exit_editor_quick ) def exit_editor_quick (self): result = askyesno('Save?', "Save file?") if result: self.save() self.exit_editor (1-result) def execute (self, name): if name is None: self.exit_editor_quick() elif name == 'Finish': self.save() self.exit_editor(0) elif name == 'Cancel': self.exit_editor(1) elif name == 'Check': self.valid() elif name == 'New PYF': self.pyf_newfile() elif name == 'Remove PYF': self.pyf_remove() def _gui2project (self): self.p.pyfs = [] for e in self.editors.values(): self.p.pyfs.append(e.get()) def save(self): self._gui2project() try: self.p.save() except: showerror('Project not saved.', 'An error prevented saving the project.') def exit_editor(self, status): "Invoked for exit and the cancel button" self.dialog.dialog.destroy() raise SystemExit, status def create_help_menu( self, main_menu): main_menu.addmenu('Help', PyfortpGui.command_summary, side='right', tearoff = 1) main_menu.addmenuitem('Help', 'command', 'Command Summary', label = 'Command Summary', command = self.evt_command_summary ) base.add_balloon_help (main_menu, 'Help') main_menu.addmenuitem('Help', 'separator') main_menu.addmenuitem('Help', 'command', 'Help About', label = 'About Pyfort', command = self.evt_about_dialog ) def evt_command_summary (self): s = Pmw.TextDialog(self.dialog.interior(), title='Command summary') s.transient(self.dialog.interior()) self.dialog.position_popup(s) s.insert('end', PyfortpGui.command_summary) def evt_about_dialog(self): Pmw.aboutversion(version.version + " executed from " + sys.exec_prefix) Pmw.aboutcopyright('Copyright:2001, Regents of the University of California\n') Pmw.aboutcontact( """Go to pyfortran.sourceforge.net for documentation, support, bug reporting, and releases. """) about = Pmw.AboutDialog(self.dialog.interior(), applicationname = 'Pyfort') about.transient(self.dialog.interior()) self.dialog.position_popup(about) # Create/Popup contributor. def create(filename): p = project.project(filename) PyfortpGui(p) base.root().mainloop() def _choice_key (choice, dict, name): if not choice in dict.values(): dict[choice] = choice for key, value in dict.items(): if value == choice: return key freeform_options=['Free form','Column 1 convention'] class PyfEditor: def __init__ (self, parent) : self.parent = parent self.filename = '' self.show_advanced = 0 f1 = Tkinter.Frame(parent) f1.pack(expand=1, fill='x', side='top', anchor='w') w = Pmw.Group(f1) self.g1 = w w.pack(side='top', anchor='w', expand=1, fill='x') self.generate_as_entry = Pmw.EntryField(w.interior(), labelpos='w', label_text='Generated Module Name', value = '' ) self.generate_as_entry.pack(side='top', anchor='w') base.balloon.bind(self.generate_as_entry, """You can name the generated extension module here, but usually it is the same as the name of the Pyfort module file. """) self.freeform_buttons = Pmw.RadioSelect(w.interior(), buttontype = 'radiobutton', orient = 'horizontal', labelpos = 'w', command = self.freeformcallback, label_text = 'PYF File Format', ) self.freeform_buttons.pack(side = 'top', anchor='w', expand = 1, fill='x') for x in freeform_options: self.freeform_buttons.add(x) self.freetag = freeform_options[0] base.balloon.bind(self.freeform_buttons, """If this Pyfort input file uses a C or c in column 1 as a comment, choose that option here. """) self.compiler_buttons = Pmw.RadioSelect(w.interior(), buttontype = 'radiobutton', orient = 'horizontal', labelpos = 'w', command = self.compilercallback, label_text = 'Target Language', ) self.compiler_buttons.pack(side = 'top', anchor='w', expand = 1, fill = 'x') for x in ['Fortran', 'C ']: self.compiler_buttons.add(x) self.c_compiler_tag = "Fortran" base.balloon.bind(self.compiler_buttons, """Choose whether this Pyfort input file will link to Fortran or C libraries. """) self.sourcetext = Pmw.ScrolledText (w.interior(), label_text="List Fortran / C Source Files Here (space delimited, wildcards ok)", labelpos='nw', usehullsize=1, hull_width=300, hull_height=100, ) self.sourcetext.pack(side='top', expand=1, fill='x') base.balloon.bind(self.sourcetext,r"""Compiled sources List here, white-space delimited, the Fortran or C sources you wish to compile into your Pyfort extension for this .pyf file. You can use wildcards. Patterns are matched using glob.glob: * matches everything ? matches any single character [seq] matches any character in seq [!seq] matches any char not in seq An initial period is not special. Both FILENAME and PATTERN are first case-normalized if the operating system requires it. You can put either C or Fortran source files here, but not both, and you must check the appropriate 'Target Language' button. Note that Pyfort is not a general tool for connecting to C; it expects Fortran-"like" routine interfaces and data types. """) w = Pmw.Group(f1, tag_pyclass = Tkinter.Button, tag_text = 'Show Advanced Options') w.pack(side='top',anchor='w', fill='x', pady=5) self.advanced_button = w.component('tag') self.advanced_button.configure(command=self.advanced_click) self.advanced_frame = Tkinter.Frame(w.interior()) self.advanced_frame.pack (fill='both', expand=1, side='top', anchor='w') self.compiler_options_entry = Pmw.EntryField(self.advanced_frame, labelpos='w', label_text='Compiler options', value = '' ) base.balloon.bind(self.compiler_options_entry, """Compiler options Put any additional options needed to compile the Fortran / C routines, such as -I include directives, or optimization directives. """) self.compiler_options_entry.pack(expand=1, side='top', anchor='w', fill='x') self.libnames_entry = Pmw.EntryField(self.advanced_frame, labelpos='w', label_text='External library names', value = '' ) self.libnames_entry.pack(expand=1, side='top', anchor='w', fill='x') libhelp = \ """If your Fortran or C is not self-contained, you may need to link with additional libraries. Put a space-delimited list of those in the 'Link libraries' box and a space-delimited list of directories holding such libraries in the 'Link directories' box. Otherwise leave them blank. The library name is as usually specified to the linker. This is the name without any prefixes or suffices, e.g. 'rs' for the library librs.a. """ base.balloon.bind(self.libnames_entry, libhelp) self.libdirs_entry = Pmw.EntryField(self.advanced_frame, labelpos='w', label_text='External library directories', value = '' ) self.libdirs_entry.pack(expand=1, side='top', anchor='w', fill='x') base.balloon.bind(self.libdirs_entry, libhelp) self.python_directory_entry = Pmw.EntryField(self.advanced_frame, labelpos='w', label_text='Directory containing Python sources', value = '' ) self.python_directory_entry.pack(expand=1, side='top', anchor='w', fill='x') base.balloon.bind(self.python_directory_entry, """If you have Python code as well as Fortran in your package, put the name of the directory that holds the Python code here. Otherwise leave it blank. """) self.package_name_entry = Pmw.EntryField(self.advanced_frame, labelpos='w', label_text='Python Package Name', value = '' ) self.package_name_entry.pack(side='top', anchor='w') base.balloon.bind(self.package_name_entry, """Set here if desired the name of the package into which to insert the generated extension. To do this, create a directory which you set in the "Python Directory" box. In the directory you place any Python files you have created to go with the pyf extension, and a file named __init__.py (two underscores on each side); typically this file either contains your Python code directly or imports the names of the functions you wish to expose to the user from your other Python files. In this case when you refer to the Fortran extension generated by the pyf file you will use packagename.extensionname. """) Pmw.alignlabels([ self.generate_as_entry, self.freeform_buttons, self.compiler_buttons, ]) Pmw.alignlabels([ self.compiler_options_entry, self.python_directory_entry, self.package_name_entry, self.libnames_entry, self.libdirs_entry, ]) self.set_advanced (0) self.generate_as_entry.component('entry').configure(bg='white') self.python_directory_entry.component('entry').configure(bg='white') self.package_name_entry.component('entry').configure(bg='white') self.libnames_entry.component('entry').configure(bg='white') self.libdirs_entry.component('entry').configure(bg='white') self.compiler_options_entry.component('entry').configure(bg='white') self.sourcetext.component('text').configure(bg='white') def advanced_click (self): self.set_advanced (1-self.show_advanced) def set_advanced (self, flag): self.show_advanced = flag if flag: self.advanced_button.configure(text='Hide Advanced Options') self.advanced_frame.pack (side='top', anchor='w', fill='x') else: self.advanced_button.configure(text='Show Advanced Options') self.advanced_frame.pack_forget() def freeformcallback(self, tag): self.freetag = tag def compilercallback(self, tag): self.c_compiler_tag = tag def put (self, pyf): self.filename = pyf.filename self.name = pyf.name self.g1.configure(tag_text=self.filename) if pyf.freeform: self.freeform_buttons.invoke(0) else: self.freeform_buttons.invoke(1) if pyf.use_c_compiler: self.compiler_buttons.invoke(1) else: self.compiler_buttons.invoke(0) self.generate_as_entry.setentry(pyf.generated_module_name()) self.python_directory_entry.setentry(pyf.python_directory) self.package_name_entry.setentry(pyf.package_name) self.compiler_options_entry.setentry(pyf.compiler_options) self.libnames_entry.setentry(pyf.libraries) self.libdirs_entry.setentry(pyf.library_directories) for x in pyf.sources: self.sourcetext.insert('end', x) self.sourcetext.insert('end','\n') self.set_advanced(pyf.is_advanced()) def get (self): if not self.filename: return None pyf = project.pyf(self.filename) x = self.python_directory_entry.component('entry').get() pyf.python_directory = x x = self.package_name_entry.component('entry').get() pyf.package_name = x x = self.libnames_entry.component('entry').get() pyf.libraries = x x = self.libdirs_entry.component('entry').get() pyf.library_directories = x x = self.compiler_options_entry.component('entry').get() pyf.compiler_options = x x = self.generate_as_entry.component('entry').get() if x != pyf.name: pyf.generate_as = x if self.freetag == freeform_options[0]: pyf.freeform = 1 else: pyf.freeform = 0 if self.c_compiler_tag == "Fortran": pyf.use_c_compiler = 0 else: pyf.use_c_compiler = 1 text = self.sourcetext.get() pyf.sources = text.split() return pyf