import twain import traceback, sys import os, os.path XferByFile='File' XferNatively='Natively' tmpfilename="tmp.bmp" OverrideXferFileName = 'c:/twainxfer.jpg' class CannotWriteTransferFile(Exception): pass class TwainBase: """Simple Base Class for twain functionality. This class should work with all the windows librarys, i.e. wxPython, pyGTK and Tk. """ SM=None # Source Manager SD=None # Data Source ProductName='SimpleTwainDemo' # Name of this product XferMethod = XferNatively # Transfer method currently in use AcquirePending = False # Flag to indicate that there is an acquire pending mainWindow = None # Window handle for the application window # Methods to be implemented by Sub-Class def LogMessage(self, message): print "****LogMessage:", message def DisplayImage(self, ImageFileName): """Display the image from a file""" print "DisplayImage:", message # End of required methods def Initialise(self): """Set up the variables used by this class""" (self.SD, self.SM) = (None, None) self.ProductName='SimpleTwainDemo' self.XferMethod = XferNatively self.AcquirePending = False self.mainWindow = None def Terminate(self): """Destroy the data source and source manager objects.""" if self.SD: self.SD.destroy() if self.SM: self.SM.destroy() (self.SD, self.SM) = (None, None) def OpenScanner(self, mainWindow=None, ProductName=None, UseCallback=False): """Connect to the scanner""" if ProductName: self.ProductName = ProductName if mainWindow: self.mainWindow = mainWindow if not self.SM: self.SM = twain.SourceManager(self.mainWindow, ProductName=self.ProductName) if not self.SM: return if self.SD: self.SD.destroy() self.SD=None self.SD = self.SM.OpenSource() if self.SD: self.LogMessage(self.ProductName+': ' + self.SD.GetSourceName()) if UseCallback: self.SM.SetCallback(self.OnTwainEvent) def _Acquire(self): """Begin the acquisition process. The actual acquisition will be notified by either polling or a callback function.""" if not self.SD: self.OpenScanner() if not self.SD: return try: self.SD.SetCapability(twain.ICAP_YRESOLUTION, twain.TWTY_FIX32, 100.0) except: pass self.SD.RequestAcquire(0, 0) # 1,1 to show scanner user interface self.AcquirePending=True self.LogMessage(self.ProductName + ':' + 'Waiting for Scanner') def AcquireNatively(self): """Acquire Natively - this is a memory based transfer""" self.XferMethod = XferNatively return self._Acquire() def AcquireByFile(self): """Acquire by file""" self.XferMethod = XferByFile return self._Acquire() def PollForImage(self): """This is a polling mechanism. Get the image without relying on the callback.""" if self.AcquirePending: Info = self.SD.GetImageInfo() if Info: self.AcquirePending = False self.ProcessXFer() def ProcessXFer(self): """An image is ready at the scanner - fetch and display it""" more_to_come = False try: if self.XferMethod == XferNatively: XferFileName=tmpfilename (handle, more_to_come) = self.SD.XferImageNatively() twain.DIBToBMFile(handle, XferFileName) twain.GlobalHandleFree(handle) self.LogMessage(self.ProductName + ':' + 'Image acquired natively') else: try: XferFileName='TWAIN.TMP' # Default rv = self.SD.GetXferFileName() if rv: (XferFileName, type) = rv # Verify that the transfer file can be produced. Security # configurations on windows can prevent it working. try: self.VerifyCanWrite(XferFileName) except CannotWriteTransferFile: self.SD.SetXferFileName(OverrideXferFileName) XferFileName = OverrideXferFileName except: # Functionality to influence file name is not implemented. # The default is 'TWAIN.TMP' pass self.VerifyCanWrite(XferFileName) self.SD.XferImageByFile() self.LogMessage(self.ProductName + ':' + "Image acquired by file (%s)" % XferFileName) self.DisplayImage(XferFileName) if more_to_come: self.AcquirePending = True else: self.SD = None except: # Display information about the exception import sys, traceback ei = sys.exc_info() traceback.print_exception(ei, ei, ei) def OnTwainEvent(self, event): """This is an event handler for the twain event. It is called by the thread that set up the callback in the first place. It is only reliable on wxPython. Otherwise use the Polling mechanism above. """ try: if event == twain.MSG_XFERREADY: self.AcquirePending = False self.ProcessXFer() elif event == twain.MSG_CLOSEDSREQ: self.SD = None except: # Display information about the exception import sys, traceback ei = sys.exc_info() traceback.print_exception(ei, ei, ei) def VerifyCanWrite(self, filepath): """The scanner can have a configuration with a transfer file that cannot be created. This method raises an exception for this case.""" parts = os.path.split(filepath) if parts: dirpart=parts else: dirpart='.' if not os.access(dirpart, os.W_OK): raise CannotWriteTransferFile, filepath
Step 1 - OpenScanner
The method 'OpenScanner' involves two steps, creating a source manager object, and creating a source object.
The source manager object is created using the call twain.SourceManager(). The TWAIN protocol uses the client's windows message queue to perform communication. To access the message queue, the clients window handle is always passed to the SourceManager constructor. The window handle is retrieved using the a toolkit specific method.
Once the source manager is opened, the client can get a data source object. The data source object is retrieve using the factory method, OpenSource. The data source object is used for all document scanning logic.
When the scanner has an image for the application, the application must find out that it is available. There are two mechanisms. The client can Poll the twain source or the application can handle an event. The mechanism for sending events uses the windows event queue. This can cause problems in Tkinter. For pyGTK or wxPython you can use either a callback or poll the source. For Tkinter or other windows libraries you must poll the source.
Step 2 - Acquire
When the client application wants to acquire a document from the scanner, it makes a request to the scanner to acquire the document. This request is available as a method of the Data Source object, called RequestAcquire. If you want the scanner user interface to display, pass (1,1) to RequestAcquire rather than (0,0).
There are two protocols, acquisition by file or acquisition natively (via windows shared memory).
Step 3 - ProcessXFer
When the data is available in the server, the client application must perform a transfer of that data from the server to the application. The application is notified that the image is ready through the 'OnTwainEvent' method or discovers the data is available by polling the source.
For native image transfer (shared memory) the image is transferred using the call XferImageNatively. XFerImageNatively returns two items (when successful), a windows handle to a global memory area, which contains the image, and an indicator to show whether there are more images pending. There will be pending images where the scanner is using a sheet-feeder.
The global memory areas are not accessible directly from Python. We use a function (in the twain module), DIBToBMFile to save the image from this global memory area to a file. DIBToBMFile stores the image which is in memory to a windows Bitmap file named tmpfilename. The application then display the image on the screen.
The application must free the global handle, using the GlobalHandleFree() method. If the application does not free these handles, the memory will be unavailable to all windows programs, even after the program exists.
For file based image transfer the image is transferred using the call XferImageByFile. XFerImageByFile creates a file containing the image.
There is a number of complications where the scanner may try to save the file to an area which cannot be written. The source code tries to retrieve the filename and handle these sort of problems. These issues are scanner device and driver specific.
This method uses a method DisplayImage to display the image. This method is implemented by the toolkit specific subclass.
Step 4 - Terminate
The user interface for the scanner software will remain displayed until the application deletes the data source object. This occurs in the MnuQuit method.
Note: The TWAIN software interfaces to your window message queue. If you close your window without deleting the Source Manager object, the TWAIN software may attempt to write messages to a deleted queue. This may cause your program to hang when it attempts to exit.