# sane.py # # Python wrapper on top of the _sane module, which is in turn a very # thin wrapper on top of the SANE library. For a complete understanding # of SANE, consult the documentation at the SANE home page: # http://www.mostang.com/sane/ . import Image import _sane from _sane import * class Option: """Class representing a SANE option. Attributes: index -- number from 0 to n, giving the option number name -- a string uniquely identifying the option title -- single-line string containing a title for the option desc -- a long string describing the option; useful as a help message type -- type of this option. Possible values: TYPE_BOOL, TYPE_INT, TYPE_STRING, and so forth. unit -- units of this option. Possible values: UNIT_NONE, UNIT_PIXEL, etc. size -- size of the value in bytes cap -- capabilities available; CAP_EMULATED, CAP_SOFT_SELECT, etc. constraint -- constraint on values. Possible values: None : No constraint (min,max,step) Integer values, from min to max, stepping by list of integers or strings: only the listed values are allowed """ def __init__(self, args): import string self.index, self.name = args[0], args[1] self.title, self.desc = args[2], args[3] self.type, self.unit = args[4], args[5] self.size, self.cap = args[6], args[7] self.constraint = args[8] def f(x): if x=='-': return '_' else: return x if type(self.name)!=type(''): self.py_name=str(self.name) else: self.py_name=string.join(map(f, self.name), '') def is_active(self): return _sane.OPTION_IS_ACTIVE(self.cap) def is_settable(self): return _sane.OPTION_IS_SETTABLE(self.cap) class SaneDev: """Class representing a SANE device. Methods: start() -- initiate a scan, using the current settings snap() -- snap a picture, returning an Image object cancel() -- cancel an in-progress scanning operation fileno() -- return the file descriptor for the scanner (handy for select) Also available, but rather low-level: get_parameters() -- get the current parameter settings of the device get_options() -- return a list of tuples describing all the options. Use .opt and .optlist attributes instead. Attributes: optlist -- list of option names opt -- a dictionary mapping option names to Option objects. You can also access an option name to retrieve its value, and to set it. For example, if one option has a .name attribute of imagemode, and scanner is a SaneDev object, you can do: print scanner.imagemode scanner.imagemode = 'Full frame' scanner.opt['imagemode'] returns the corresponding Option object. """ def __init__(self, devname): d=self.__dict__ d['dev']=_sane._open(devname) d['opt']={} optlist=d['dev'].get_options() for t in optlist: o=Option(t) if o.type!=TYPE_GROUP: d['opt'][o.py_name]=o def __setattr__(self, key, value): dev=self.__dict__['dev'] optdict=self.__dict__['opt'] if not optdict.has_key(key): self.__dict__[key]=value ; return opt=optdict[key] if opt.type==TYPE_GROUP: raise AttributeError, "Groups can't be set: "+key if not _sane.OPTION_IS_ACTIVE(opt.cap): raise AttributeError, 'Inactive option: '+key if not _sane.OPTION_IS_SETTABLE(opt.cap): raise AttributeError, "Option can't be set by software: "+key self.last_opt = dev.set_option(opt.index, value) def __getattr__(self, key): dev=self.__dict__['dev'] optdict=self.__dict__['opt'] if key=='optlist': return self.opt.keys() if not optdict.has_key(key): raise AttributeError, 'No such attribute: '+key opt=optdict[key] if opt.type==TYPE_BUTTON: raise AttributeError, "Buttons don't have values: "+key if opt.type==TYPE_GROUP: raise AttributeError, "Groups don't have values: "+key if not _sane.OPTION_IS_ACTIVE(opt.cap): raise AttributeError, 'Inactive option: '+key self.last_opt, value = dev.get_option(opt.index) return value def get_parameters(self): "Return a tuple holding all the current device settings" return self.dev.get_parameters() def get_options(self): "Return a list of tuples describing all the available options" return self.dev.get_options() def start(self): "Initiate a scanning operation" return self.dev.start() def cancel(self): "Cancel an in-progress scanning operation" return self.dev.cancel() def snap(self): "Snap a picture, returning a PIL image object with the results" format, last_frame, (xsize, ysize), depth, bytes_per_line = self.get_parameters() if (format=='G') or (format=='R') or (format=='B'): format='L' im=Image.new(format, (xsize,ysize)) self.dev.snap( im.im.id ) return im def fileno(self): "Return the file descriptor for the scanning device" return self.dev.fileno() def open(devname): "Open a device for scanning" new=SaneDev(devname) return new