[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

gEDA-cvs: CVS update: config.py.in



  User: pcjc2   
  Date: 06/09/19 05:42:42

  Added:       .        config.py.in funcs.py gsch2pcbproject.py gui.py
                        pcbmanager.py
  Log:
  Initial commit of xgsch2pcb
  
  
  
  
  Revision  Changes    Path
  1.1                  eda/geda/xgsch2pcb/lib/xgsch2pcb/config.py.in
  
  Index: config.py.in
  ===================================================================
  # -*-Python-*-
  
  # Autogenerated from config.py.in during configure process
  
  # xgsch2pcb - a GUI for gsch2pcb
  # Copyright (C) 2006 University of Cambridge
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or
  # (at your option) any later version.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  PACKAGE="@PACKAGE@"
  VERSION="@VERSION@"
  prefix="@prefix@"
  pkglibdir="@pkglibdir@"
  prefix="@prefix@"
  
  
  
  
  1.1                  eda/geda/xgsch2pcb/lib/xgsch2pcb/funcs.py
  
  Index: funcs.py
  ===================================================================
  # -*-Python-*-
  
  # xgsch2pcb - a GUI for gsch2pcb
  # Copyright (C) 2006 University of Cambridge
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or
  # (at your option) any later version.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  import commands
  import os
  
  def find_tool_path (tool):
  
      which = commands.getstatusoutput("which %s" % tool)
      if which[0]:
          return None
      
      return which[1]
      
  def rel_path(topath, fromdir=""):
  
      sep = os.path.sep
      pardir = os.path.pardir
  
      # Expand paths:
      # "" becomes the current dir
      # /foo/../ sections are contracted
      fromdir = os.path.abspath( fromdir )
      topath = os.path.abspath( topath )
  
      if fromdir == topath:
          return ''
  
      # We need a directory separator at the end for consistency
      if not fromdir.endswith( sep ):
          fromdir = fromdir + sep
  
      # Compute the common prefix of the paths
      # Note, that this is done on a string basis:
      # /home and /homedir share a common prefix of /home
      prefix = os.path.commonprefix([fromdir, topath])
      
      # If the topath doesn't have fromdir as a common part: 
      if (prefix != fromdir):
  
          # Go down one directory in the fromdir, and prepend
          # a relative move down one dir in the output string
          fromdir = os.path.abspath( fromdir + pardir )
  
          # Recurse down
          return pardir + sep + rel_path(fromdir, topath)
      
      # Return the topath without the common prefix
      return topath[len(prefix):]
  
  
  
  
  1.1                  eda/geda/xgsch2pcb/lib/xgsch2pcb/gsch2pcbproject.py
  
  Index: gsch2pcbproject.py
  ===================================================================
  # -*-Python-*-
  
  # xgsch2pcb - a GUI for gsch2pcb
  # Copyright (C) 2006 University of Cambridge
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or
  # (at your option) any later version.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  import os, gobject
  
  class Gsch2PCBProject(gobject.GObject):
  
      __gsignals__ = { 'dirty-flag-changed' :
                              ( gobject.SIGNAL_NO_RECURSE,
                                gobject.TYPE_NONE,
                                (gobject.TYPE_BOOLEAN, )),
                       'page-added' :
                              ( gobject.SIGNAL_NO_RECURSE,
                                gobject.TYPE_NONE,
                                (gobject.TYPE_STRING, )),
                       'page-removed' :
                              ( gobject.SIGNAL_NO_RECURSE,
                                gobject.TYPE_NONE,
                                (gobject.TYPE_STRING, )),
                     }
                     
      def __init__(self, filename=None, output_name=None):
          gobject.GObject.__init__(self)
  
          self.filename = filename
          self.dirty = False
          self.pages = []
  
          if output_name != None:
              self.output_name = output_name
          elif filename != None:
              # Try something clever...
              self.output_name = filename.rsplit('.', 1)[0]
              if self.output_name == "":
                  self.output_name = filename
          else:
              self.output_name = None
  
          if os.path.exists(self.filename):
              self.load()
  
      def set_dirty(self, flag=True):
          if (self.dirty != flag):
              self.dirty = flag
              self.emit('dirty-flag-changed', flag)
  
      def load(self, fromfile=None):
          if fromfile == None:
              fromfile = self.filename
          if fromfile == None:
              raise Exception, 'No filename specified to load'
  
          fp = open(fromfile, 'rb')
          for line in fp:
              parts = line.strip().split()
              opt = parts[0]
              if opt == 'schematics':
                  self.pages = parts[1:]
              elif opt == 'output-name':
                  self.output_name = parts[1]
              else:
                  raise Exception, 'Unsupported project file option: %s' % line
          fp.close()
          if fromfile == self.filename:
              self.set_dirty(False)
  
      def save(self, destfile=None):
          if destfile == None:
              destfile = self.filename
          if destfile == None:
              raise Exception, 'No filename specified for project'
  
          fp = open(destfile, 'wb')
          fp.write('schematics %s\n' % ' '.join(self.pages))
          fp.write('output-name %s\n' % self.output_name)
          fp.close()
          if destfile == self.filename:
              self.set_dirty(False)
  
      def add_page(self, filename):
          if not filename in self.pages:
              self.pages.append(filename)
              self.emit('page-added', filename)
              self.set_dirty()
  
      def remove_page(self, filename):
          if filename in self.pages:
              self.pages.remove(filename)
              self.emit('page-removed', filename)
              self.set_dirty()
  
  gobject.type_register( Gsch2PCBProject )
  
  
  
  
  1.1                  eda/geda/xgsch2pcb/lib/xgsch2pcb/gui.py
  
  Index: gui.py
  ===================================================================
  # -*-Python-*-
  
  # xgsch2pcb - a GUI for gsch2pcb
  # Copyright (C) 2006 University of Cambridge
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or
  # (at your option) any later version.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  import gtk, gtk.gdk, gobject, os, sys, commands, shutil
  from stat import *
  from subprocess import *
  
  # i18n
  import gettext
  t = gettext.translation('xgsch2pcb', fallback=True)
  _ = t.ugettext
  
  # xgsch2pcb-specific modules
  from funcs import *
  from gsch2pcbproject import Gsch2PCBProject
  from pcbmanager import PCBManager
  
  try:
      import gnomevfs
  except:
      # We won't be able to launch URLs
      pass
  
  class MonitorWindow(gtk.Window):
  
      # ======================================================================== #
      # Initialisers
      # ======================================================================== #
      
      def __init__(self, project=None):
          gtk.Window.__init__(self)
  
          self.project = None
          self.pcbmanager = None
  
          self.set_title("xgsch2pcb")
          self.set_size_request(400,300)
          self.set_events(gtk.gdk.FOCUS_CHANGE_MASK)
          self.connect("focus-in-event", self.event_focused)
          self.connect("delete_event", self.event_delete)
  
          mainvbox = gtk.VBox(False, 5)
          self.add(mainvbox)
  
          # Initialize toolbar
          self.__init_toolbar__(mainvbox)
  
          # Hbox contains two vboxes, one for page editing widgets, one
          # for layout editing widgets
          
          hbox = gtk.HBox(True, 5)
          mainvbox.pack_start(hbox)
  
          # Page editing widgets
          # --------------------
          frame = gtk.Frame(_("Schematic pages"))
          frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
          hbox.add(frame)
  
          vbox = gtk.VBox(False, 3)
          vbox.set_border_width(5)
          frame.add(vbox)
  
          scrollwin = gtk.ScrolledWindow()
          scrollwin.set_policy (gtk.POLICY_AUTOMATIC,
                                gtk.POLICY_AUTOMATIC)
          vbox.pack_start(scrollwin, True, True)
          
          # Treeview showing available schematic pages
          self.pagelist = gtk.TreeView(gtk.ListStore(str))
          scrollwin.add_with_viewport(self.pagelist)
          column = gtk.TreeViewColumn(None, gtk.CellRendererText(), text=0)
          self.pagelist.append_column(column)
          selection = self.pagelist.get_selection()
          selection.set_mode(gtk.SELECTION_SINGLE)
          selection.connect('changed',
                            self.event_pagelist_selection_changed)
          self.pagelist.set_headers_visible(False)
          
          # Horizontal button box containing 'add page' and 'remove page'
          # buttons
          addremovebox = gtk.HButtonBox()
          addremovebox.set_layout(gtk.BUTTONBOX_SPREAD)
          vbox.pack_start(addremovebox, False, True)
  
          self.addpagebutton = gtk.Button(stock=gtk.STOCK_ADD)
          addremovebox.pack_start(self.addpagebutton)
          self.addpagebutton.connect("clicked",
                         self.event_addpage_button_clicked)
          
          self.removepagebutton = gtk.Button(stock=gtk.STOCK_REMOVE)
          self.removepagebutton.connect("clicked",
                                        self.event_removepage_button_clicked)
          addremovebox.pack_start(self.removepagebutton)
          
          # Buttons to run gschem/gattrib
          self.editpagebutton = gtk.Button(_("Edit schematic"))
          vbox.pack_start(self.editpagebutton, False, True)
  
          # TODO: Is this wanted? The padding seems wrong
          #image = gtk.Image()
          #image.set_from_stock(gtk.STOCK_EDIT,gtk.ICON_SIZE_BUTTON)
          #self.editpagebutton.set_image(image)
  
          self.editpagebutton.connect("clicked",
                         self.event_schematic_button_clicked,
                         "gschem")
          
          self.attribpagebutton = gtk.Button(_("Edit attributes"))
          vbox.pack_start(self.attribpagebutton, False, True)
          
          # TODO: Is this wanted? The padding seems wrong
          #image = gtk.Image()
          #image.set_from_stock(gtk.STOCK_EDIT,gtk.ICON_SIZE_BUTTON)
          #self.attribpagebutton.set_image(image)
  
          self.attribpagebutton.connect("clicked",
                         self.event_schematic_button_clicked,
                         "gattrib")
  
  
          # Layout editing widgets
          # ----------------------
          frame = gtk.Frame(_("Layout"))
          frame.set_shadow_type(gtk.SHADOW_ETCHED_IN)
          hbox.add(frame)
  
          vbox = gtk.VBox(False, 3)
          vbox.set_border_width(5)
          frame.add(vbox)
  
          self.pcbentry = gtk.Entry()
          self.pcbentry.set_property('editable', False)
          vbox.pack_start(self.pcbentry, False, True)
  
          self.editpcbbutton = gtk.Button(_("Edit layout"))
          vbox.pack_start(self.editpcbbutton, False, False)
          self.editpcbbutton.connect("clicked",
                         self.event_editpcb_button_clicked)
  
          self.updatepcbbutton = gtk.Button(_("Update layout"))
          vbox.pack_start(self.updatepcbbutton, False, False)
          self.updatepcbbutton.connect("clicked",
                         self.event_updatepcb_button_clicked)
  
          self.changepcbbutton = gtk.Button(_("Change layout file"))
          vbox.pack_start(self.changepcbbutton, False, False)
          self.changepcbbutton.connect("clicked",
                         self.event_changepcb_button_clicked)
  
  
          def about_url_cb(dialog, link, user_data):
              try:
        			    gnomevfs.url_show( link )
              except:
                  pass
  
          # About dialog
          # ------------
          self.aboutdialog = gtk.AboutDialog()
          self.aboutdialog.set_name(_("xgsch2pcb - a GUI for gsch2pcb"))
          self.aboutdialog.set_version("1.0")
          self.aboutdialog.set_copyright("University of Cambridge 2006")
          self.aboutdialog.set_authors(['Peter Brett', 'Peter Clifton'])
          gtk.about_dialog_set_url_hook(about_url_cb, None)
          self.aboutdialog.set_website('http://geda.seul.org/')
  
          self.pcbmanager = None
          self.set_project(project)
  
  
      def __init_toolbar__(self, box):
          toolbar = gtk.Toolbar()
          box.pack_start(toolbar, False, True)
          self.toolbar_buttons = {}
  
          button = gtk.ToolButton(gtk.STOCK_QUIT)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_quit_button_clicked) 
          self.toolbar_buttons['quit'] = button
  
          toolbar.insert(gtk.SeparatorToolItem(), -1)
          
          button = gtk.ToolButton(gtk.STOCK_NEW)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_new_button_clicked)
          self.toolbar_buttons['new'] = button
  
          button = gtk.ToolButton(gtk.STOCK_OPEN)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_open_button_clicked)
          self.toolbar_buttons['open'] = button
  
          button = gtk.ToolButton(gtk.STOCK_SAVE)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_save_button_clicked)
          self.toolbar_buttons['save'] = button
  
          #button = gtk.ToolButton(gtk.STOCK_SAVE_AS)
          #toolbar.insert(button, -1)
          #button.connect("clicked",
          #               self.event_saveas_button_clicked)
          #self.toolbar_buttons['saveas'] = button
  
          button = gtk.ToolButton(gtk.STOCK_CLOSE)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_close_button_clicked)
          self.toolbar_buttons['close'] = button
  
          #button = gtk.ToolButton(gtk.STOCK_PROPERTIES)
          #toolbar.insert(button, -1)
          #button.connect("clicked",
          #               self.event_properties_button_clicked)
          #self.toolbar_buttons['close'] = button
          #button.set_sensitive(False) #FIXME
  
          toolbar.insert(gtk.SeparatorToolItem(), -1)
          
          button = gtk.ToolButton(gtk.STOCK_ABOUT)
          toolbar.insert(button, -1)
          button.connect("clicked",
                         self.event_about_button_clicked)
          self.toolbar_buttons['about'] = button
  
  
      # ======================================================================== #
      # Signal handlers
      # ======================================================================== #
  
      def event_project_dirty_changed(self, project, dirty):
          self.update_title()
  
      def event_project_page_added(self, project, page):
          model = self.pagelist.get_model()
          model.append([page])
          self.set_pcbsensitivities()
  
      def event_project_page_removed(self, project, page):
          model = self.pagelist.get_model()
          iter = model.get_iter_first()
          while iter:
              if model.get_value(iter, 0) == page:
                  model.remove(iter)
                  break
              iter = model.iter_next(iter)
          self.set_pcbsensitivities()
  
      def event_pagelist_selection_changed(self, selection):
          (model, iter) = selection.get_selected()
          page_selected = (iter != None)
          self.removepagebutton.set_sensitive(page_selected)
          self.editpagebutton.set_sensitive(page_selected)
          self.attribpagebutton.set_sensitive(page_selected)
          
      def event_addpage_button_clicked(self, button):
          numpages = len(self.project.pages)
          pagename = '%s-page%s.sch' % (self.project.output_name,
                                        numpages+1)
  
          add_dialog = AddPageDialog(self, pagename)
          while (True):
              add_dialog.show_all()
              r = add_dialog.run()
  
              if r != gtk.RESPONSE_ACCEPT:
                  break
  
              from_file = add_dialog.is_from_existing()
              filename = add_dialog.get_filename()
              if filename == None:
                  md = gtk.MessageDialog(self,
                                         (gtk.DIALOG_MODAL |
                                          gtk.DIALOG_DESTROY_WITH_PARENT),
                                         gtk.MESSAGE_ERROR,
                                         gtk.BUTTONS_OK,
                                         _('You must select either an existing schematic file or enter a filename for a new file.'))
                  md.show_all()
                  md.run()
                  md.hide_all()
                  continue
  
              # If the user's specified a schematic that's not in a
              # subdirectory of the project directory, complain.
              if rel_path(filename).startswith('../'):
                  md = gtk.MessageDialog(self,
                                         (gtk.DIALOG_MODAL |
                                          gtk.DIALOG_DESTROY_WITH_PARENT),
                                         gtk.MESSAGE_WARNING,
                                         gtk.BUTTONS_NONE)
  
                  md.set_markup(_('<span weight="bold" size="larger">Selected file is outside the project directory\nAdd anyway?</span>\n\nProjects are best kept in self contained directories. Ensure that you don\'t move or delete any external files, or the project will be incomplete.'))
  
                  # Set GUI spacings
                  md.set_border_width( 6 )
                  md.vbox.set_spacing( 12 )
                  #md.hbox.border_width( 6 )
                  #md.hbox.set_spacing( 12 )
  
                  md.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
                  button = gtk.Button(_("_Add anyway"))
                  image = gtk.Image()
                  image.set_from_stock( gtk.STOCK_ADD, gtk.ICON_SIZE_BUTTON )
                  button.set_image( image )
                  md.add_action_widget(button, gtk.RESPONSE_ACCEPT)
  
                  md.show_all()
                  r = md.run()
                  md.hide_all()
                  
                  if r != gtk.RESPONSE_ACCEPT:
                      continue
  
              filename = rel_path(filename)
  
              if not os.path.exists(filename):
                  # Create a new zero-length file in place
                  open(filename, 'w').close()
              self.project.add_page(filename)
              break
  
          add_dialog.hide_all()
  
  
      def event_removepage_button_clicked(self, button):
          (model, iter) = self.pagelist.get_selection().get_selected()
          if iter == None:
              return
          page = model.get_value(iter, 0)
          self.project.remove_page(page)
  
      def event_schematic_button_clicked(self, button, tool):
          (model, iter) = self.pagelist.get_selection().get_selected()
          page = model.get_value(iter, 0)
  
          toolpath = find_tool_path(tool)
          if toolpath == None:
              md = gtk.MessageDialog(self,
                                     (gtk.DIALOG_MODAL |
                                      gtk.DIALOG_DESTROY_WITH_PARENT),
                                     gtk.MESSAGE_ERROR,
                                     gtk.BUTTONS_OK,
                                     _('Could not locate tool: %s') % tool)
              md.show_all()
              md.run()
              md.hide_all()
              return
  
          Popen([toolpath, page])
  
      def event_editpcb_button_clicked(self, button):
          
          # Check if the layout might need updating
          if self.pcbmanager.needs_updating( self.project.pages ):
          
              # Ask if the user wants to update the layout
              d = gtk.MessageDialog(self,
                                    (gtk.DIALOG_MODAL | 
                                     gtk.DIALOG_DESTROY_WITH_PARENT),
                                    gtk.MESSAGE_QUESTION,
                                    gtk.BUTTONS_NONE,
                                    _("Your schematic has changed.\n\nWould you like to update your PCB layout?"))
              d.add_buttons(_("Leave layout unchanged"), gtk.RESPONSE_REJECT,
                            _("Update layout"), gtk.RESPONSE_ACCEPT)
              d.show_all()
              do_update = (d.run() == gtk.RESPONSE_ACCEPT)
              d.hide_all()
  
              if do_update:
                  # Update the layout (leaving PCB open)
                  self.update_layout()
                  return
          
          # TODO: Catch any exceptions which might prevent this working
          self.pcbmanager.open_layout()
  
      def event_updatepcb_button_clicked(self, button):
          # Update the layout (leaving PCB open)
          self.update_layout()
      
      # TODO: Implement me
      def event_changepcb_button_clicked(self, button):
  
          d = gtk.MessageDialog(self,
                                (gtk.DIALOG_MODAL | 
                                 gtk.DIALOG_DESTROY_WITH_PARENT),
                                gtk.MESSAGE_INFO,
                                gtk.BUTTONS_OK)
          d.set_markup(_('<span weight="bold" size="larger">Unimplemented feature</span>\n\nRenaming the layout file not implemented'))
          d.show_all()
          d.run()
          d.hide_all()
  
  
  
      def event_new_button_clicked( self, button ):
  
          if self.close_project( _("creating a new project")):
              # User cancelled out of creating a new project
              return
  
          d = gtk.FileChooserDialog(_('New project...'), self,
                                (gtk.DIALOG_MODAL | 
                                 gtk.DIALOG_DESTROY_WITH_PARENT),
                                (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                 gtk.STOCK_NEW, gtk.RESPONSE_OK))
          filter = gtk.FileFilter()
          filter.add_pattern('*.gsch2pcb')
          d.set_filter(filter)
          d.set_do_overwrite_confirmation( True )
          d.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
  
          d.show_all()
          r = d.run()
          d.hide_all()
  
          if r != gtk.RESPONSE_OK:
              return
  
          path = d.get_filename()
          if not path.endswith('.gsch2pcb'):
              path += '.gsch2pcb'
  
         	self.set_project(path)
          self.project.save()
  
  
      def event_open_button_clicked( self, button ):
         
          reason = _("opening a new project")
  
          # If tools are open there is no point asking the user a filename
          if self.check_no_tools( reason ):
              return
  
          filter = gtk.FileFilter()
          filter.add_pattern('*.gsch2pcb')
          
          fcd = gtk.FileChooserDialog(_('Open Project...'), self,
                                      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
                                      (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                                       gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT))
          fcd.set_show_hidden (False)
          fcd.set_filter (filter)
          fcd.set_action (gtk.FILE_CHOOSER_ACTION_OPEN)
          fcd.set_local_only (True)
  
          fcd.show_all()
          r = fcd.run()
          fcd.hide_all()
  
          if r != gtk.RESPONSE_ACCEPT:
              return
          
          filename = fcd.get_filename()
          
          # The user has an option to cancel here
          if self.close_project( reason ):
              return
         
          self.set_project( filename )
  
  
      def event_save_button_clicked( self, button ):
          self.project.save()
          
      ## TODO: Implement me
      #def event_saveas_button_clicked( self, button ):
      #    pass
  
      def event_close_button_clicked( self, button ):
          self.close_project( _("closing the project") )
  
      def event_quit_button_clicked(self, button):
          self.handle_quit()
  
      def event_about_button_clicked(self, button):
          self.aboutdialog.show_all()
          self.aboutdialog.run()
          self.aboutdialog.hide_all()
  
      def event_focused(self, window, direction):
          self.set_pcbsensitivities()
  
      def event_delete(self, window, event):
          return self.handle_quit()
  
      # ======================================================================== #
      # General utility methods
      # ======================================================================== #
  
      def update_title( self ):
  
          if self.project:
              title = os.path.split(self.project.filename)[1]
              if self.project.dirty:
                  title += _(' [modified]')
              title += " - "
      
          else:
              title = ''
          
          title += "xgsch2pcb"
          self.set_title(title)
  
  
      def set_projectsensitivities(self):
  
          projectman = not ( self.project == None )
  
          widget_list = ( self.toolbar_buttons['save'],
                          #self.toolbar_buttons['saveas'],
                          self.toolbar_buttons['close'],
                          self.pagelist,
                          self.addpagebutton )
  
          for widget in widget_list:
              widget.set_sensitive( projectman )
  
  
      def set_pcbsensitivities(self):
          
          projectman = not ( self.project == None )
          pcbmanager = not ( self.pcbmanager == None )
  
          if pcbmanager and self.pcbmanager.is_layout_open():
              pcbrunning = True
          else:
              pcbrunning = False
  
          if self.project and len( self.project.pages ) > 0:
              pages_available = True
          else:
              pages_available = False
  
          managers = projectman and pcbmanager
  
          self.pcbentry.set_sensitive( managers )
          self.editpcbbutton.set_sensitive( managers and (not pcbrunning) )
          self.updatepcbbutton.set_sensitive( managers and pages_available )
          self.changepcbbutton.set_sensitive( managers )
  
  
      def handle_quit (self):
          if self.close_project( _("exiting") ):
              return True
          gtk.main_quit()
  
  
      def set_project(self, filename):
          assert self.pcbmanager == None
          
          if filename == None:
              self.project = None
          else:
              
              dirname = os.path.dirname(filename)
              if dirname:
                  os.chdir(dirname)
  
              self.project = Gsch2PCBProject(filename)
              self.project.connect('dirty-flag-changed',
                                   self.event_project_dirty_changed)
              self.project.connect('page-added',
                                   self.event_project_page_added)
              self.project.connect('page-removed',
                                   self.event_project_page_removed)
  
              # TODO FIXME mangle for i18n support
              self.pcbentry.set_text(rel_path(self.project.output_name + ".pcb"))
  
              try:
                  self.pcbmanager = PCBManager(self.project)
              # TODO: Subclass Exception to be more specific in PCBManager
              except Exception, (instance):
                  message = str( instance )
                  md = gtk.MessageDialog(self,
                                         (gtk.DIALOG_MODAL |
                                          gtk.DIALOG_DESTROY_WITH_PARENT),
                                         gtk.MESSAGE_ERROR,
                                         gtk.BUTTONS_OK )
  
                  md.set_markup( _('<span weight="bold" size="larger">Problem initialising</span>\n%s') % message )
                  md.show_all()
                  md.run()
                  md.hide_all()
              
          # TODO set model for self.pagelist
          pagelistmodel = self.pagelist.get_model()
          pagelistmodel.clear()
  
          if self.project:
              for page in self.project.pages:
                  pagelistmodel.append([page])
  
          selection = self.pagelist.get_selection()
          selection.unselect_all()
          selection.emit('changed')
  
          self.set_projectsensitivities()
          self.set_pcbsensitivities()
  
          # TODO: Could this trigger on a signal?
          self.update_title()
  
      def check_no_tools( self, reason = None ):
          """
          Prompts the user, and teturns true if there is still a tool open
          """
  
          # If the layout is still open, don't allow the project to close
          if self.pcbmanager and self.pcbmanager.is_layout_open():
              d = gtk.MessageDialog(self,
                                (gtk.DIALOG_MODAL | 
                                 gtk.DIALOG_DESTROY_WITH_PARENT),
                                gtk.MESSAGE_ERROR,
                                gtk.BUTTONS_OK)
              if reason:
                  d.set_markup( 
                      _('<span weight="bold" size="larger">Layout editor still open</span>\n\nClose the layout editor before %s.') % reason)
              else:
                  d.set_markup( 
                      _('<span weight="bold" size="larger">Layout editor still open</span>\n\nClose the layout editor first.'))
                 
              # Set GUI spacings
              d.set_border_width( 6 )
              d.vbox.set_spacing( 12 )
              d.show_all()
              d.run()
              d.hide_all()
  
              return True
  
          return False
  
      def close_project( self, reason = None ):
          """
          Returns true if for some reason we wish to cancel the close operation
          """
          
          # If the layout is still open, don't allow the project to close
          if self.pcbmanager and self.pcbmanager.is_layout_open():
              d = gtk.MessageDialog(self,
                                (gtk.DIALOG_MODAL | 
                                 gtk.DIALOG_DESTROY_WITH_PARENT),
                                gtk.MESSAGE_ERROR,
                                gtk.BUTTONS_OK)
              if reason:
                  d.set_markup( 
                      _('<span weight="bold" size="larger">Layout editor still open</span>\n\nClose the layout editor before %s.') % reason)
              else:
                  d.set_markup( 
                      _('<span weight="bold" size="larger">Layout editor still open</span>\n\nClose the layout editor first.'))
                 
              # Set GUI spacings
              d.set_border_width( 6 )
              d.vbox.set_spacing( 12 )
              d.show_all()
              d.run()
              d.hide_all()
  
              return True
  
          # Check if any tools are open, prompting the user if so
          if self.check_no_tools( reason ):
              return True
  
          # TODO: Where should this go? in check_no_tools perhaps?
          self.pcbmanager = None
  
          if self.project and self.project.dirty:
              md = gtk.MessageDialog(self,
                                     (gtk.DIALOG_MODAL |
                                      gtk.DIALOG_DESTROY_WITH_PARENT),
                                     gtk.MESSAGE_WARNING,
                                     gtk.BUTTONS_NONE)
  
              md.set_markup(_('<span weight="bold" size="larger">Save the changes to project "%s" before closing?</span>\n\nAny changes made since the last save will be lost.') % self.project.filename)
  
              # Set GUI spacings
              md.set_border_width( 6 )
              md.vbox.set_spacing( 12 )
              md.add_buttons( _("Close _without Saving"), gtk.RESPONSE_CLOSE,
                                        gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                                        gtk.STOCK_SAVE, gtk.RESPONSE_OK )
              md.show_all()
              r = md.run()
              md.hide_all()
              if r == gtk.RESPONSE_OK:
                  # TODO: Need to attempt a save, possibly using a save box
                  # if the save is cancelled, we must also cancel the close
  
                  # Save the project
                  self.project.save()
  
              elif r != gtk.RESPONSE_CLOSE:
                  # User doesn't cancelled the dialog
                  return True
  
          # TODO: ACTUALLY CLOSE THE PROJECT
          self.set_project( None )
  
          return False
  
      def update_layout( self ):
          # TODO: Catch any exceptions which might prevent this working
          self.pcbmanager.update_layout( self.project.pages )
          
  gobject.type_register( MonitorWindow )
  
  class AddPageDialog(gtk.Dialog):
      def __init__(self, parent, defaultfilename="untitled.sch"):
          gtk.Dialog.__init__(self, _('Add schematic page...'), parent,
                              (gtk.DIALOG_MODAL |
                               gtk.DIALOG_DESTROY_WITH_PARENT),
                              (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                               gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
          
  
          table = gtk.Table(2,2)
          
          # GUI Spacing
          self.set_has_separator( False )
          self.set_border_width( 0 )
          table.set_row_spacings( 6 )
          
          # GUI Spacing
          alignment = gtk.Alignment( 0, 0, 0, 0 )
          alignment.set_padding( 12, 18, 6, 6 )
          alignment.add( table )
          
          self.vbox.pack_start( alignment )
  
          # Two radio buttons allow you to select whether to use an
          # existing file or create a new file
          self.fileradio = gtk.RadioButton(label=_('From file:'))
          self.fileradio.connect('toggled', self.event_radio_toggled)
          
          # GUI Spacing
          alignment = gtk.Alignment( 0, 0, 0, 0 )
          alignment.set_padding( 0, 0, 0, 12 )
          alignment.add( self.fileradio )
  
          table.attach(alignment, 0, 1, 0, 1, gtk.FILL, 0)
  
          self.newradio = gtk.RadioButton(self.fileradio, _('Create new:'))
          self.newradio.connect('toggled', self.event_radio_toggled)
          
          # GUI Spacing
          alignment = gtk.Alignment( 0, 0, 0, 0 )
          alignment.set_padding( 0, 0, 0, 12 )
          alignment.add( self.newradio )
  
          table.attach(alignment, 0, 1, 1, 2, gtk.FILL, 0)
  
          # File chooser button to select and existing file.  Currently
          # limited to local files 'cos gsch2pcb can't handle remote
          # files.
          self.filebutton = gtk.FileChooserButton(_('Select schematic page...'))
          self.filebutton.connect( "selection-changed", self.event_filebutton_selection_changed )
          self.filebutton.set_local_only(True)
          self.filebutton.set_action(gtk.FILE_CHOOSER_ACTION_OPEN)
          schfilter = gtk.FileFilter()
          schfilter.add_pattern('*.sch')
          self.filebutton.set_filter(schfilter)
          table.attach(self.filebutton, 1, 2, 0, 1, gtk.FILL | gtk.EXPAND, 0)
  
          # Text entry field to enter the filename of a new schematic
          # page to create
          self.newentry = gtk.Entry()
          self.newentry.set_text(defaultfilename)
          table.attach(self.newentry, 1, 2, 1, 2, gtk.FILL | gtk.EXPAND, 0)
  
          self.fileradio.set_active(True)
          self.fileradio.emit('toggled')
  
          # TODO: Get bug fixed in GTK+, or find proper solution
  
          # Workaround possible bug in GTK+
          self.last_filename = None
  
      def event_filebutton_selection_changed( self, filebutton ):
          
          # Unfortunatly (a bug in GTK+ perhaps?), where the 
          # "selection-changed" event is sometimes fired when 
          # the user CANCELS from the file-chooser (if an existing
          # filename was present when we started)
  
          filename = self.filebutton.get_filename()
  
          # Workaround the bug if the filename hasn't changed
  
          if filename == self.last_filename:
              # Still isn't perfect, if the user selects opening
              # the same file, they must then click "Ok" on our
              # dialog.
              return
  
          self.last_filename = filename
  
          self.response( gtk.RESPONSE_ACCEPT )
          
      
      def event_radio_toggled(self, button):
          is_file = self.is_from_existing()
          self.filebutton.set_sensitive(is_file)
          self.newentry.set_sensitive(not is_file)
          
      def is_from_existing(self):
          return self.fileradio.get_active()
      
      def get_filename(self):
          if self.is_from_existing():
              return self.filebutton.get_filename()
          else:
              return self.newentry.get_text()
  
  gobject.type_register ( AddPageDialog )
  
  # TODO: Need a mechanism to open a new project as a "NEW" project, when a
  #       project with that filename already exists on disk. This is for
  #       over-writing an existing project of the same name.
  
  class NewProjectDialog(gtk.FileChooserDialog):
      def __init__(self, parent):
          gtk.FileChooserDialog.__init__(self, _('New project...'), parent,
                              gtk.FILE_CHOOSER_ACTION_SAVE,
                              (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
                               gtk.STOCK_NEW, gtk.RESPONSE_ACCEPT))
          
          # TODO: Might add a more complex interface for a generic project manager
  
          # TODO: Decide if this is worth the high dependancy of PyGTK 2.8+
          self.set_do_overwrite_confirmation( True )
  
          # TODO: Decide if this is worth the high dependancy of PyGTK 2.8+
          self.connect( "confirm-overwrite", self.signal_confirm_overwrite )
  
  	filter = gtk.FileFilter()
          filter.add_pattern('*.gsch2pcb')
          self.set_filter(filter)
  
      
      # TODO: Decide if this is worth the high dependancy of PyGTK 2.8+
      # TODO: Implement a dialog to prompt about
      #       over-writing an existing project
      def signal_confirm_overwrite( self, filechooser ):
          
          md = gtk.MessageDialog(self,
                                 (gtk.DIALOG_MODAL |
                                  gtk.DIALOG_DESTROY_WITH_PARENT),
                                 gtk.MESSAGE_WARNING,
                                 gtk.BUTTONS_NONE)
  
          # TODO: Split to just give the filename
          filename = self.get_filename()
          dirname = self.get_current_folder()
  
          md.set_markup(_('<span weight="bold" size="larger">A project named "%s" already exists. Do you want to replace it?</span>\n\nThe project already exists in directory "%s". Replacing it will overwrite its contents.') % ( filename, dirname ))
  
          # Set GUI spacings
          md.set_border_width( 6 )
          md.vbox.set_spacing( 12 )
          #md.hbox.border_width( 6 )
          #md.hbox.set_spacing( 12 )
  
          md.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT)
          
          button = gtk.Button( _("_Replace") )
          image = gtk.Image()
          image.set_from_stock( gtk.STOCK_SAVE_AS, gtk.ICON_SIZE_BUTTON )
          button.set_image( image )
          md.add_action_widget(button, gtk.RESPONSE_ACCEPT)
  
          md.show_all()
          r = md.run()
          md.hide_all()
          
          if r != gtk.RESPONSE_ACCEPT:
              return gtk.FILE_CHOOSER_CONFIRMATION_SELECT_AGAIN
          
          return gtk.FILE_CHOOSER_CONFIRMATION_ACCEPT_FILENAME
  
  gobject.type_register( NewProjectDialog )
  
  
  
  1.1                  eda/geda/xgsch2pcb/lib/xgsch2pcb/pcbmanager.py
  
  Index: pcbmanager.py
  ===================================================================
  # -*-Python-*-
  
  # xgsch2pcb - a GUI for gsch2pcb
  # Copyright (C) 2006 University of Cambridge
  #
  # This program is free software; you can redistribute it and/or modify
  # it under the terms of the GNU General Public License as published by
  # the Free Software Foundation; either version 2 of the License, or
  # (at your option) any later version.
  #
  # This program is distributed in the hope that it will be useful,
  # but WITHOUT ANY WARRANTY; without even the implied warranty of
  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  # GNU General Public License for more details.
  #
  # You should have received a copy of the GNU General Public License
  # along with this program; if not, write to the Free Software
  # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  # TODO: Is this needed? What version of python do we depend on?
  from __future__ import generators
  
  import commands, shutil, re, gobject, os, sys
  
  # i18n
  import gettext
  t = gettext.translation('xgsch2pcb', fallback=True)
  _ = t.ugettext
  
  # xgsch2pcb-specific modules
  from stat import *
  from subprocess import *
  from funcs import *
  
  # Define PCB action return codes
  PCB_RC_OK      = 001
  PCB_RC_ERROR   = 002
  
  class PCBManager( gobject.GObject ):
  
      __gsignals__ = { "update-complete" :
                        ( 0,                        # No special flags
                          gobject.TYPE_NONE,        # Return type
                          (gobject.TYPE_BOOLEAN, )  # Pass a bool to signal
                         ) }
      
      def __init__(self, project):
          gobject.GObject.__init__(self)
  
          self.project = project
         
          # TODO: Need to think about whether these are relative or absolute paths
          self.project_dir = self.project.filename.rsplit('.', 1)[0]
          self.output_name = self.project.output_name
  
          self.pipes = None
          self.cofunc = None
          
          self.toolpath = find_tool_path( 'pcb' )
          self.gsch2pcbpath = find_tool_path( 'gsch2pcb' )
  
          if not (self.toolpath and self.gsch2pcbpath):
              exception_txt = ''
              if not self.toolpath:
                  exception_txt = exception_txt + \
                      _("\nCouldn't find 'pcb' executable")
              if not self.gsch2pcbpath:
                  exception_txt = exception_txt + \
                      _("\nCouldn't find 'gsch2pcb' executable")
              raise Exception, exception_txt
  
  
  
      def __del__(self):
         
          if self.pcb_stdin:
              self.pcb_stdin.close()
  
          if self.pcb_stdout:
              self.pcb_stdout.close()
  
          # TODO: Close pipe, kill PCB and call parent class del
          if not self.pipes == None:
              self.pipes == None
  
          # TODO: Do we need to call our parent's destructor?
          #gobject.GObject.__del__(self)
  
      def close_layout(self):
  
          # TODO: Fixme
          self.pcb_write( "Quit()\n" )
          
          # TODO: Block until the pipe dies, or timeout?
          pass
  
      def open_layout(self):
          
          if self.is_layout_open():
              # TODO: Is there some clever way to bring PCB to front?
              # Possibly send it an action which does a window-manager request?
              return
          
          pcbargs = (self.toolpath, '--listen', self.output_name + ".pcb" )
          self.pipes = Popen(pcbargs, stdin=PIPE, stdout=PIPE)
          self.pcb_stdout = self.pipes.stdout
          self.pcb_stdin = self.pipes.stdin
          
          # TODO: Wait until PCB confirms layout loaded
          
          ## Call fnctl to set non-blocking IO on the input stream
          #child_in = pcbpipe.stdout.fileno()
          #flags = fcntl.fcntl(child_in, fcntl.F_GETFL)
          #fcntl.fcntl(child_in, fcntl.F_SETFL, flags | os.O_NONBLOCK)
          
          # Setup callback for data ready on PCB's stdout pipe
          self.parser = pcb_output_parser( self.pcb_stdout )
          self.parser.connect("return-code", self.parser_return_code_cb)
          self.parser.connect("pipe-died", self.parser_pipe_died_cb)
  
          assert self.is_layout_open()
  
      def is_layout_open(self):
         
          if self.pipes == None or self.pipes.poll() != None:
              return False
  
          # TODO: Check to see if the layout is actually open, not just pipe alive?
          return True
  
      def update_layout(self, schematics):
  
          # First check the layout is open
          if not self.is_layout_open():
              self.open_layout()
  
          if self.cofunc == None:
              self.cofunc = self.cofunction_update( schematics )
          else:
              print _("Already in the middle of an update")
              # TODO: Some further exception?
              return
          
          try:
              self.cofunc.next()
                  
          except StopIteration:
              self.cofunc = None
      
          
      def needs_updating(self, schematics):
          
          # In the future, this could save the users currently active layout
          # and then check if it is complete (via gsch2pcb), thus not relying
          # on mtime.
          
          # If the PCB layout file doesn't exist, assume that it needs updating
          if not os.path.exists(self.output_name + ".pcb"):
              if len( schematics ) > 0:
                  return True
              else:
                  return False
          
          layout_mtime = os.stat(self.output_name + ".pcb")[ST_MTIME]
  
          schematic_mtime = layout_mtime
          for page in schematics:
              mtime = os.stat(page)[ST_MTIME]
              schematic_mtime = max(mtime, schematic_mtime)
  
          return (layout_mtime < schematic_mtime)
  
      def pcb_write( self, string ):
          self.pcb_stdin.write( string )
          print ">pcb<: ", string,
  
      def cofunction_update( self, schematics ):
  
          # NOTE TO HACKERS:
          #
          # This function (rightly or wrongly) is implemeted as a python generator.
          # It uses the generator function "yield", to return out of this function
          # whilst allowing re-entry at that point after a suitable signal is handled.
          #
          # After every yield statement, we should check the status-codes from the
          # parser class. This is done by calling self.parser.consume_retcode().
          #
          # For convenience, call "error_occurred()", which returns True if an error
          # condition was met.
  
          def error_occurred( ):
              retcode = self.parser.consume_retcode()
              error = False
              if retcode == None:
                  error = True
                  print _("error_occurred: no retcode from pcb parser")
              elif retcode == PCB_RC_ERROR:
                  error = True
                  print _("error_occurred: Error response, retcode=%s") % retcode
              return error
  
          def error_restore_backup():
              # Deliberatly leave (self.output_name + ".savedbackup.pcb") incase it is useful
              
              # Move original file backup to layout if it exists
              if os.path.exists(self.output_name + ".backup.pcb"):
                  shutil.move(self.output_name + ".backup.pcb", self.output_name + ".pcb")
  
          def cleanup_files():
  
              # Move backup back to layout file if it exists
              try:
                  shutil.move(self.output_name + ".backup.pcb", self.output_name + ".pcb")
              except:
                  pass
              
              # Remove backup before gsch2pcb
              try:
                  os.remove(self.output_name + ".savedbackup.pcb")
              except:
                  pass
                  
              # Clean up gsch2pcb's mess
              try:
                  os.remove(self.output_name + ".tmp.gsch2pcb")
              except:
                  pass
  
              try:
                  os.remove(self.output_name + ".new.pcb")
              except:
                  pass
  
              try:
                  os.remove(self.output_name + ".cmd")
              except:
                  pass
  
  
          print _("********START UPDATING********")
          
          # Send 'hello' to PCB
          self.pcb_write("Hello()\n")
          
          # Wait to re-enter with a response
          yield None
  
          if error_occurred():
              # PCB is not responding happily to its "Hello" action
              # TODO: ERROR MESSAGE TO THE USER
              return
              
          # TODO: TELL PCB TO IGNORE USER ACTIONS
  
          # Move current layout to backup if it exists
          if os.path.exists(self.output_name + ".pcb"):
              shutil.move(self.output_name + ".pcb", self.output_name + ".backup.pcb")
          
          # Save layout
          self.pcb_write("SaveTo(Layout, %s)\n" % (self.output_name + ".pcb"))
          
          # Wait to re-enter with a response
          yield None
          
          if error_occurred():
              error_restore_backup()
              # TODO: TELL PCB TO ALLOW USER ACTIONS
              # TODO: ERROR MESSAGE TO USER
              return
  
          # Copy saved layout to backup
          shutil.copy(self.output_name + ".pcb", self.output_name + ".savedbackup.pcb")
          
          # Create gsch2pcb project file
          self.project.save( self.output_name + ".tmp.gsch2pcb" )
          
          # Run gsch2pcb
          # TODO: Handle via Popen like other tools?
          gsch2pcb_cmd = self.gsch2pcbpath + " -q " + self.output_name + ".tmp.gsch2pcb"
          gsch2pcb_output = commands.getstatusoutput(gsch2pcb_cmd)
          lines = gsch2pcb_output[1].splitlines()
          for line in lines:
              print "<gsch2pcb>:", line
  
          # TODO: HANDLE ERROR OUTPUT FROM gsch2pcb!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
          
          # Load layout
          self.pcb_write("LoadFrom(Revert, %s)\n" % (self.output_name + ".pcb"))
          
          # Wait to re-enter with a response
          yield None
          
          if error_occurred():
              error_restore_backup()
              # TODO: TELL PCB TO ALLOW USER ACTIONS
              # TODO: ERROR MESSAGE TO USER
              return
          
          # Delete rats
          self.pcb_write("DeleteRats(AllRats)\n")
          
          # Load netlist
          self.pcb_write("LoadFrom(Netlist, %s)\n" % (self.output_name + ".net"))
  
          # Wait to re-enter with a response
          yield None
          
          if error_occurred():
              # TODO: WARNING TO THE USER?
              pass
          
          # If new elements exist, put them in the paste-buffer
          newparts = self.output_name + ".new.pcb"
          if os.path.exists (newparts):
              self.pcb_write("LoadFrom(LayoutToBuffer, %s)\n" % newparts)
              
              # Wait to re-enter with a response
              yield None
              
              if error_occurred():
                  # TODO: WARN USER?
                  pass
         
              # Paste the new components near the origin
              self.pcb_write("PasteBuffer(ToLayout,10,10,mil)\n")
              
              # Change back to the "none" (select) tool
              self.pcb_write("Mode(None)\n")
          
          # Run the .cmd file
          self.pcb_write("ExecuteFile(%s)\n" % (self.output_name + ".cmd"))
          
          # Wait to re-enter with a response
          yield None
          
          if error_occurred():
              # TODO: WARN USER?
              pass
              
          # Add the rat-lines
          self.pcb_write("AddRats(AllRats)\n")
          
          # Move original layout backup back in place, delete intermediate files
          cleanup_files()
  
          print _("********DONE UPDATING********")
              
      
      def parser_return_code_cb( self, parser, retcode ):
       
          # If running, advance the function which is communicating with PCB
  
          if self.cofunc == None:
              # print _("Ignoring return code out of cofunc execution")
              return
              
          try:
              self.cofunc.next()
                  
          except StopIteration:
              self.cofunc = None
  
      ## TODO: Use this timeout handler somewhere?
      #def pcb_timeout_cb( self ):
      #
      #    if self.cofunc == None:
      #        return True
      #
      #    try:
      #        self.cofunc.next()
      #
      #    except StopIteration:
      #        self.cofunc = None
      #
      #    ## OR, should we just:
      #    #self.parser_return_code_cb( self.parser, 'TIMEOUT' )
      #    ## as this gives a definate indication of re-entry cause
  
  
      def parser_pipe_died_cb( self, parser ):
          
          # Close the pipe
          self.pcb_stdin.close()
          self.pcb_stdin = None
  
          # Close the corresponding pipe outputting to PCB
          self.pcb_stdout.close()
          self.pcb_stdout = None
  
          # Close any remaining attachment to the process
          # TODO: Check there isn't an equivelant to "Pclose"
          self.pipes = None
              
  gobject.type_register( PCBManager )
  
  class pcb_output_parser(gobject.GObject):
      # Register signals which we may emit
      __gsignals__ = { "return-code": ( gobject.SIGNAL_NO_RECURSE,
                                        gobject.TYPE_NONE,
                                        (gobject.TYPE_INT, ) ),
                       "pipe-died": ( gobject.SIGNAL_NO_RECURSE,
                                        gobject.TYPE_NONE,
                                        () ) }
                                      
  
      def __init__(self, file):
          gobject.GObject.__init__(self)
          
          self.atnewline = True
          self.file = file
  
          # Hardcode these for now
          self.out_prefix = "<pcb>: "
          self.out_echo = True
  
          self.last_retcode = None
  
          # Compile a regular expression to get any numerical 
          # return code from a PCB output string
          self.retcode_re = re.compile( '([0-9]{3,3}) - ' )
          
          self.watch_handle = gobject.io_add_watch(self.file, 
                               gobject.IO_IN | gobject.IO_ERR | gobject.IO_HUP,
                               self.pcb_read_cb )
      
      def __del__(self):
          gobject.source_remove(self.watch_handle);
          gobject.GObject.__del__(self)
      
      def consume_retcode(self):
          retval = self.last_retcode
          self.last_retcode = None
          return retval
          
      def get_last_retcode(self):
          return self.last_retcode
          
      def pcb_read_cb (self, source, condition ):
          
          # Check if the connection has died
          if condition & gobject.IO_HUP:
  
              # Callback signal to inform the program
              self.emit( "pipe-died" );
  
              # Return False, so the io_watch will not re-run
              return False
          
          # Attempt reading from the pipe
          try:
  
              data = self.file.readline ()
  
          except IOError:
  
              print >> sys.stderr, _("pcb_read_cb: IOError whilst in readline()")
  
              # Callback signal to inform the program
              self.emit( "pipe-died" );
  
              # Return False, so the io_watch will not re-run
              return False
          
          if self.out_echo:
              print self.out_prefix, data,
  
          # TODO: Decide if we just want to signal directly with the line read from PCB?
  
          re_match = self.retcode_re.match( data )
          
          if re_match:
              self.last_retcode = int( re_match.group(1) )
              self.emit ( "return-code", self.last_retcode )
          
          return True
  
  gobject.type_register( pcb_output_parser )
   
  
  
  


_______________________________________________
geda-cvs mailing list
geda-cvs@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-cvs