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)