/ lib / python / gladevcp / hal_filechooser.py
hal_filechooser.py
  1  #!/usr/bin/env python
  2  # vim: sts=4 sw=4 et
  3  # GladeVcp FileChooser related widgets
  4  #
  5  # Copyright (c) 2010  Pavel Shramov <shramov@mexmat.net>
  6  #
  7  # This program is free software: you can redistribute it and/or modify
  8  # it under the terms of the GNU General Public License as published by
  9  # the Free Software Foundation, either version 2 of the License, or
 10  # (at your option) any later version.
 11  #
 12  # This program is distributed in the hope that it will be useful,
 13  # but WITHOUT ANY WARRANTY; without even the implied warranty of
 14  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15  # GNU General Public License for more details.
 16  
 17  import os, sys, time, select, re
 18  import tempfile, atexit, shutil
 19  
 20  import gtk, gobject
 21  
 22  from hal_widgets import _HalWidgetBase
 23  import linuxcnc
 24  from hal_glib import GStat
 25  
 26  _ = lambda x: x
 27  
 28  from hal_actions import _EMC_ActionBase, _EMC_Action
 29  
 30  progress_re = re.compile("^FILTER_PROGRESS=(\\d*)$")
 31  class FilterProgram:
 32      def __init__(self, program_filter, infilename, outfilename, callback=None):
 33          import subprocess
 34          outfile = open(outfilename, "w")
 35          infilename_q = infilename.replace("'", "'\\''")
 36          env = dict(os.environ)
 37          env['AXIS_PROGRESS_BAR'] = '1'
 38          p = subprocess.Popen(["sh", "-c", "%s '%s'" % (program_filter, infilename_q)],
 39                                stdin=subprocess.PIPE,
 40                                stdout=outfile,
 41                                stderr=subprocess.PIPE,
 42                                env=env)
 43          p.stdin.close()  # No input for you
 44          self.p = p
 45          self.stderr_text = []
 46          self.program_filter = program_filter
 47          self.callback = callback
 48          gobject.timeout_add(100, self.update)
 49          #progress = Progress(1, 100)
 50          #progress.set_text(_("Filtering..."))
 51  
 52      def update(self):
 53          if self.p.poll() is not None:
 54              self.finish()
 55              return False
 56  
 57          r,w,x = select.select([self.p.stderr], [], [], 0)
 58          if not r:
 59              return True
 60          stderr_line = self.p.stderr.readline()
 61          m = progress_re.match(stderr_line)
 62          if m:
 63              pass #progress.update(int(m.group(1)), 1)
 64          else:
 65              self.stderr_text.append(stderr_line)
 66              sys.stderr.write(stderr_line)
 67          return True
 68  
 69      def finish(self):
 70          # .. might be something left on stderr
 71          for line in self.p.stderr:
 72              m = progress_re.match(line)
 73              if not m:
 74                  self.stderr_text.append(line)
 75                  sys.stderr.write(line)
 76          r = self.p.returncode
 77          if r:
 78              self.error(r, "".join(self.stderr_text))
 79          if self.callback:
 80              self.callback(r)
 81  
 82      def error(self, exitcode, stderr):
 83          dialog = gtk.MessageDialog(None, 0, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE,
 84                  _("The program %(program)r exited with code %(code)d.  "
 85                  "Any error messages it produced are shown below:")
 86                      % {'program': self.program_filter, 'code': exitcode})
 87          dialog.format_secondary_text(stderr)
 88          dialog.run()
 89          dialog.destroy()
 90  
 91  class _EMC_FileChooser(_EMC_ActionBase):
 92      def _hal_init(self):
 93          _EMC_ActionBase._hal_init(self)
 94          self.ini = None
 95          self.tmp = None
 96          self.prefilter_path = None
 97          self.load_filters()
 98  
 99      def mktemp(self):
100          if self.tmp:
101              return
102          self.tmp = tempfile.mkdtemp(prefix='emcflt-', suffix='.d')
103          atexit.register(lambda: shutil.rmtree(self.tmp))
104  
105      def load_file(self, filename):
106          self.prefilter_path = filename
107          flt = self.get_filter_program(filename)
108          if not flt:
109              return self._load_file(filename)
110  
111          if not self.tmp:
112              self.mktemp()
113  
114          tmp = os.path.join(self.tmp, os.path.basename(filename))
115          flt = FilterProgram(flt, filename, tmp, lambda r: r or self._load_file(tmp))
116  
117      def _load_file(self, filename):
118          if filename:
119              self.linuxcnc.mode(linuxcnc.MODE_AUTO)
120              old = self.gstat.stat.file
121              self.linuxcnc.program_open(filename)
122              if old == filename:
123                  self.gstat.emit('file-loaded', filename)
124  
125      def refilter(self):
126          if self.prefilter_path is None:
127              return
128          self.load_file(self.prefilter_path)
129  
130      def load_filters(self, inifile=None):
131          inifile = inifile or os.environ.get('INI_FILE_NAME', '/dev/null')
132          self.ini = linuxcnc.ini(inifile)
133  
134          self._load_filters(self.ini)
135  
136      def get_filter_program(self, filename):
137          ext = os.path.splitext(filename)[1]
138          if ext:
139              return self.ini.find("FILTER", ext[1:])
140          else:
141              return None
142  
143      def _load_filters(self, inifile):
144          def _e2p(n, el):
145              #print "New filter %s: %s" % (n, el)
146              p = gtk.FileFilter()
147              p.set_name(n)
148              map(lambda s: p.add_pattern('*' + s), el)
149              #print p
150              return p
151          all_extensions = [".ngc"]
152          extensions = inifile.findall("FILTER", "PROGRAM_EXTENSION")
153          extensions = [e.split(None, 1) for e in extensions]
154          extensions = tuple([(v, tuple(k.split(","))) for k, v in extensions])
155          map(lambda t: all_extensions.extend(t[1]), extensions)
156          self.add_filter(_e2p("All machinable files", all_extensions))
157          self.add_filter(_e2p("rs274ngc files", ['.ngc']))
158          for n,e in extensions:
159              self.add_filter(_e2p(n, e))
160          self.add_filter(_e2p("All files", ['']))
161  
162  class EMC_FileChooserDialog(gtk.FileChooserDialog, _EMC_FileChooser):
163      __gtype_name__ = 'EMC_FileChooserDialog'
164      def __init__(self, *a, **kw):
165          gtk.FileChooserDialog.__init__(self, *a, **kw)
166          _EMC_FileChooser._hal_init(self)
167          self.connect('response', self.on_response)
168  
169      def on_response(self, w, response):
170          pass
171          #print ">>>", w, response
172  
173  class EMC_FileChooserButton(gtk.FileChooserButton, _EMC_FileChooser):
174      __gtype_name__ = 'EMC_FileChooserButton'
175      def __init__(self, *a, **kw):
176          gtk.FileChooserButton.__init__(self, gtk.FileChooserDialog())
177  
178          self.connect('file-set', self.on_file_set)
179  
180      def on_file_set(self, w):
181          self.load_file(w.get_filename())
182  
183  class EMC_Action_Open(_EMC_Action, _EMC_FileChooser):
184      __gtype_name__ = 'EMC_Action_Open'
185      fixed_file = gobject.property(type=str, default='', nick='Fixed file name')
186  
187      def _hal_init(self):
188          _EMC_FileChooser._hal_init(self)
189          _EMC_Action._hal_init(self)
190          self.currentfolder = os.path.expanduser("~/linuxcnc/nc_files")
191          self.gstat.connect('interp-run', lambda w: self.set_sensitive(False))
192          self.gstat.connect('interp-idle', lambda w: self.set_sensitive(True))
193  
194      def _load_filters(self, ini): pass
195  
196      def on_activate(self, w):
197          if self.fixed_file:
198              self.load_file(self.fixed_file)
199              return
200          dialog = EMC_FileChooserDialog(title="Open File",action=gtk.FILE_CHOOSER_ACTION_OPEN, 
201                  buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
202          dialog.set_current_folder(self.currentfolder)
203          dialog.show()
204          r = dialog.run()
205          fn = dialog.get_filename()
206          dialog.hide()
207          if r == gtk.RESPONSE_OK:
208              dialog.load_file(fn)
209              self.currentfolder = os.path.dirname(fn)
210          dialog.destroy()
211  
212  class EMC_Action_Reload(_EMC_Action, _EMC_FileChooser):
213      __gtype_name__ = 'EMC_Action_Reload'
214      def _hal_init(self):
215          _EMC_Action._hal_init(self)
216  
217      def on_activate(self, w):
218          self._load_file(self.gstat.stat.file)