text: # -*- coding: utf-8 -*-
  """
  Created on Fri Dec  2 23:48:33 2016
  @author: yxl
  """
  
  import threading
  import numpy as np
  from time import time, sleep
  
  def process_channels(plg, ips, src, des, para):
      if ips.channels>1 and not 'not_channel' in plg.note:
          for i in range(ips.channels):
              rst = plg.run(ips, src if src is None else src[:,:,i], des[:,:,i], para)
              if not rst is des and not rst is None:
                  des[:,:,i] = rst
      else:
          rst = plg.run(ips, src, des, para)
          if not rst is des and not rst is None:
              des[:] = rst
      return des
  
  def process_one(plg, ips, src, img, para, callafter=None):
      plg.app.add_task(plg)
      start = time()
      transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16)
      transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64)
      if transint: 
          buf = img.astype(np.int32)
          src = src.astype(np.int32)
      if transfloat: 
          buf = img.astype(np.float32)
          src = src.astype(np.float32)
      rst = process_channels(plg, ips, src, buf if transint or transfloat else img, para)
      if not img is rst and not rst is None:
          imgrange = {np.uint8:(0,255), np.uint16:(0, 65535)}[img.dtype.type]
          np.clip(rst, imgrange[0], imgrange[1], out=img)
      if 'auto_msk' in plg.note and not ips.mask('out') is None:
          msk = ips.mask('out')
          img[msk] = src[msk]
      plg.app.info('%s: cost %.3fs'%(ips.title, time()-start))
      ips.update()
      plg.app.remove_task(plg)
      if not callafter is None:callafter()
      
  def process_stack(plg, ips, src, imgs, para, callafter=None):
      plg.app.add_task(plg)
      start = time()
      transint = '2int' in plg.note and ips.dtype in (np.uint8, np.uint16)
      transfloat = '2float' in plg.note and not ips.dtype in (np.complex128, np.float32, np.float64)
      if transint: 
          buf =  imgs[0].astype(np.int32)
          src = src.astype(np.int32)
      elif transfloat: 
          buf = imgs[0].astype(np.float32)
          src = src.astype(np.float32)
      else: src = src * 1
  
      for i,n in zip(imgs,list(range(len(imgs)))):
          #sleep(0.5)
          plg.progress(n, len(imgs))
          if 'auto_snap' in plg.note : src[:] = i
          if transint or transfloat: buf[:] = i
          rst = process_channels(plg, ips, src, buf if transint or transfloat else i, para)
          if not i is rst and not rst is None:
              imgrange = {np.uint8:(0,255), np.uint16:(0,65535)}[i.dtype.type]
              np.clip(rst, imgrange[0], imgrange[1], out=i)
          if 'auto_msk' in plg.note and not ips.mask() is None:
              msk = ips.mask('out')
              i[msk] = src[msk]
      plg.app.info('%s: cost %.3fs'%(ips.title, time()-start))
      ips.update()
      plg.app.remove_task(plg)
      if not callafter is None:callafter()
      
  
  class Filter:
      title = 'Filter'
      modal = True
      note = []
      'all, 8-bit, 16-bit, int, rgb, float, not_channel, not_slice, req_roi, auto_snap, auto_msk, preview, 2int, 2float'
      para = None
      view = None
      prgs = None
  
      def __init__(self, ips=None): pass
          
      def progress(self, i, n): self.prgs = int(i*100/n)
  
      def show(self):
          preview = lambda para, ips=self.ips: self.preview(ips, para) or ips.update()
          return self.app.show_para(self.title, self.view, self.para, preview, 
              on_ok=lambda : self.ok(self.ips), on_help=self.on_help,
              on_cancel=lambda : self.cancel(self.ips) or self.ips.update(), 
              preview='preview' in self.note, modal=self.modal)
      
      def run(self, ips, snap, img, para = None):
          return 255-img
          
      def check(self, ips):
          note = self.note
          if ips == None:
              return self.app.alert('No image opened!')
              return False
          elif 'req_roi' in note and ips.roi == None:
              return self.app.alert('No Roi found!')
          elif not 'all' in note:
              if ips.dtype==np.uint8 and ips.channels==3 and not 'rgb' in note:
                  return self.app.alert('Do not surport rgb image')
              elif ips.dtype==np.uint8 and ips.channels==1 and not '8-bit' in note:
                  return self.app.alert('Do not surport 8-bit image')
              elif ips.dtype==np.uint16 and not '16-bit' in note:
                  return self.app.alert('Do not surport 16-bit uint image')
              elif ips.dtype in [np.int32, np.int64] and not 'int' in note:
                  return self.app.alert('Do not surport int image')
              elif ips.dtype in [np.float32, np.float64] and not 'float' in note:
                  return self.app.alert('Do not surport float image')
          return True
          
      def preview(self, ips, para):
          process_one(self, ips, ips.snap, ips.img, para, None)
          
      def load(self, ips):return True
            
      def ok(self, ips, para=None, callafter=None):
          if para == None:
              para = self.para
              if not 'not_slice' in self.note and ips.slices>1:
                  if para == None:para = {}
              if para!=None and 'stack' in para:del para['stack']
          # = WidgetsManager.getref('Macros Recorder')
          if ips.slices==1 or 'not_slice' in self.note:
              # process_one(self, ips, ips.snap, ips.img, para)
              threading.Thread(target = process_one, args = 
                  (self, ips, ips.snap, ips.img, para, callafter)).start()
              # if win!=None: win.write('{}>{}'.format(self.title, para))
              self.app.record_macros('{}>{}'.format(self.title, para))
          elif ips.slices>1:
              has, rst = 'stack' in para, None
              if not has:
                  rst = self.app.yes_no('Run every slice in current stacks?')
              if 'auto_snap' in self.note and self.modal:ips.reset()
              if has and para['stack'] or rst == 'yes':
                  para['stack'] = True
                  #process_stack(self, ips, ips.snap, ips.imgs, para)
                  threading.Thread(target = process_stack, args = 
                      (self, ips, ips.snap, ips.imgs, para, callafter)).start()
                  self.app.record_macros('{}>{}'.format(self.title, para))
              elif has and not para['stack'] or rst == 'no': 
                  para['stack'] = False
                  #process_one(self, ips, ips.snap, ips.img, para)
                  threading.Thread(target = process_one, args = 
                      (self, ips, ips.snap, ips.img, para, callafter)).start()
                  self.app.record_macros('{}>{}'.format(self.title, para))
              elif rst == 'cancel': pass
          #ips.update()
          
      def on_help(self):
          self.app.show_md(self.__doc__ or 'No Document!', self.title)
  
      def cancel(self, ips):
          if 'auto_snap' in self.note:
              ips.img[:] = ips.snap
              ips.update()
              
      def start(self, app, para=None, callafter=None):
          self.app, self.ips = app, app.get_img()
          if not self.check(self.ips):return
          if not self.load(self.ips):return
          if 'auto_snap' in self.note:self.ips.snapshot()
          
          if para!=None:
              self.ok(self.ips, para, callafter)
          elif self.view==None:
              if not self.__class__.show is Filter.show:
                  if self.show():
                      self.ok(self.ips, para, callafter)
              else: self.ok(self.ips, para, callafter)
          elif self.modal:
              if self.show():
                  self.ok(self.ips, None, callafter)
              else:self.cancel(self.ips)
          else: self.show()
  
      def __del__(self):
          print('filter del')
  