Advertisement
nuit

annYmage

Mar 20th, 2011
232
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 23.95 KB | None | 0 0
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3.  
  4.  
  5. import pygame
  6. import math
  7. import sys
  8. import subprocess as sp
  9. import threading
  10. import time
  11. import os
  12. import re
  13. import tempfile
  14. import shutil
  15. import types
  16. import ctypes
  17. from ctypes.util import find_library
  18.  
  19. pygame.display.init()
  20. pygame.font.init()
  21.  
  22.  
  23. CAPTION_H = 25
  24. STATUS_H = 25
  25. DIST = 10
  26. TEXT_PADDING = 30
  27.  
  28. SIGINT = 2
  29.  
  30. SCREEN_WIDTH = 1200
  31. SCREEN_HEIGHT = 690
  32.  
  33. SAVE_DIRECTORY = "/home/jan/Photos/Camera"
  34. #SAVE_DIRECTORY = "/Users/anny/Pictures/annYmage"
  35.  
  36. class ImageProviderDirectory(threading.Thread):
  37.     def __init__(self, directory):
  38.         threading.Thread.__init__(self)
  39.         self.directory = directory
  40.  
  41.     def terminate(self):
  42.         self.stop = True
  43.  
  44.     def shoot(self):
  45.         pass
  46.  
  47.     def run(self):
  48.         self.list = []
  49.         self.stop = False
  50.  
  51.         while not self.stop:
  52.             time.sleep(1)
  53.             new = filter(lambda x: x not in self.list,
  54.                          os.listdir(self.directory))
  55.             App.getInstance().dispatch('load',
  56.                 map(lambda x: os.path.join(self.directory, x), new))
  57.             self.list += new
  58.  
  59. class ImageProviderExternal(threading.Thread):
  60.     def __init__(self):
  61.         threading.Thread.__init__(self)
  62.  
  63.     def terminate(self):
  64.         self.send('quit', True)
  65.  
  66.         # We could alternatively use a combination of poll and sleep to avoid a deadlock
  67.         self.prog.wait()
  68.  
  69.     def shoot(self):
  70.         self.send('shoot', True)
  71.  
  72.     def mode(self):
  73.         self.send('mode', True)
  74.  
  75.     def send(self, msg, immediate=False):
  76.         self.prog.stdin.write(msg + "\n")
  77.         if immediate: self.prog.send_signal(SIGINT)
  78.  
  79.     def run(self):
  80.         # start external program
  81.         self.prog = sp.Popen(['./cameraprovider.py'], stdin=sp.PIPE, stdout=sp.PIPE)
  82.  
  83.         for line in self.prog.stdin:
  84.             (cmd, args) = line.rstrip('\r\n').split(' ', 1)
  85.             print "Firing: %s(%s)" % (cmd, args)
  86.             App.getInstance().dispatch(cmd, args)
  87.  
  88.  
  89. #############
  90. #
  91. # ImageProviderCamera
  92. # - runs in a separate thread
  93. # - communicates with the camera via libgphoto
  94. # - filenames of new photos are send to ImageViewer via userevent
  95. #
  96. #############
  97.  
  98.  
  99. # gphoto structures
  100.  
  101. class CameraFilePath(ctypes.Structure):
  102.     _fields_ = [('name', (ctypes.c_char * 128)),
  103.                 ('folder', (ctypes.c_char * 1024))]
  104.  
  105. (GP_EVENT_UNKNOWN,
  106.  GP_EVENT_TIMEOUT,
  107.  GP_EVENT_FILE_ADDED,
  108.  GP_EVENT_FOLDER_ADDED,
  109.  GP_EVENT_CAPTURE_COMPLETE) = range(5)
  110. class CameraEventType(ctypes.c_int): pass
  111.  
  112.  
  113. GP_OK = 0
  114. GP_CAPTURE_IMAGE = 0
  115. GP_FILE_TYPE_NORMAL = 1
  116.  
  117. class ImageProviderCamera(threading.Thread):
  118.     def __init__(self, manual=False):
  119.         threading.Thread.__init__(self)
  120.  
  121.         self.todo = threading.Condition()
  122.         self.queued_shots = 0
  123.         self.running = True
  124.         self.manual = manual
  125.  
  126.         self.fn_no = 0
  127.  
  128.     #####
  129.     # External signals
  130.     #####
  131.     def terminate(self):
  132.         with self.todo:
  133.             self.running = False
  134.             self.todo.notify()
  135.  
  136.     def shoot(self):
  137.         with self.todo:
  138.             self.queued_shots += 1
  139.             self.todo.notify()
  140.  
  141.     def mode(self):
  142.         with self.todo:
  143.             self.manual = not self.manual
  144.             App.getInstance().dispatch("mode", self.manual and "manual" or "automatic")
  145.             self.todo.notify()
  146.  
  147.     #####
  148.     # Aux
  149.     #####
  150.     def next_fn(self, cam_file):
  151.         self.fn_no += 1
  152.         suffix = cam_file.rsplit('.',1)[-1]
  153.         return os.path.join(self.directory, "img-%s-%d.%s" % (time.strftime("%H%M%S"), self.fn_no, suffix))
  154.  
  155.     def status(self, s):
  156.         print "status: %s" % s
  157.         App.getInstance().dispatch('status', s)
  158.  
  159.     #####
  160.     # Camera Controlling
  161.     #####
  162.  
  163.     def kill_others(self):
  164.         for i in map(lambda x: x.strip().split(' ',1), sp.Popen(['/bin/ps', '-eo', 'pid,command'], stdout=sp.PIPE).communicate()[0].splitlines()):
  165.             if('PTPCamera.app' in i[1]):
  166.                 os.kill(int(i[0]), 2)
  167.                 time.sleep(1)
  168.  
  169.     def init_camera(self):
  170.         self.kill_others()
  171.  
  172.         # Init camera
  173.         self.status('Initialising camera ...')
  174.         self.context = self.gp.gp_context_new()
  175.         self.camera = ctypes.c_void_p()
  176.         self.gp.gp_camera_new(ctypes.byref(self.camera))
  177.         self.gp.gp_camera_init(self.camera, self.context)
  178.         event = CameraEventType()
  179.         data_p = ctypes.c_void_p()
  180.         self.working = (self.gp.gp_camera_wait_for_event
  181.                         (self.camera,
  182.                          100,
  183.                          ctypes.byref(event),
  184.                          ctypes.byref(data_p),
  185.                          self.context) == GP_OK)
  186.         print "returned event %d" % event.value
  187.  
  188.     def release_camera(self):
  189.         # Release the camera
  190.         self.status('Releasing the camera')
  191.         self.gp.gp_camera_exit(self.camera, self.context)
  192.         self.gp.gp_camera_unref(self.camera)
  193.  
  194.  
  195.     def download(self, cam_path):
  196.  
  197.         # download
  198.         self.status('downloading ...')
  199.         fn = self.next_fn(cam_path.name)
  200.  
  201.         cam_file = ctypes.c_void_p()
  202.         fd = os.open(fn, os.O_CREAT | os.O_WRONLY)
  203.         self.gp.gp_file_new_from_fd(ctypes.pointer(cam_file), fd)
  204.         if self.gp.gp_camera_file_get(self.camera,
  205.                               cam_path.folder,
  206.                               cam_path.name,
  207.                               GP_FILE_TYPE_NORMAL,
  208.                               cam_file,
  209.                               self.context) != GP_OK:
  210.             print "Download failed"
  211.             self.working = False
  212.         else:
  213.             print "Downloaded %s in %s" % (cam_path.name, fn)
  214.             App.getInstance().dispatch('load', [fn])
  215.  
  216.             # delete
  217.             self.status('deleting ...')
  218.             self.gp.gp_camera_file_delete(self.camera,
  219.                                  cam_path.folder,
  220.                                  cam_path.name,
  221.                                  self.context)
  222.             print "Deleted %s from camera" % cam_path.name
  223.  
  224.         self.gp.gp_file_unref(cam_file)
  225.  
  226.  
  227.     def capture(self):
  228.         self.status('shooting ...')
  229.  
  230.         # Capture image
  231.         cam_path = CameraFilePath()
  232.         if self.gp.gp_camera_capture(self.camera,
  233.                          GP_CAPTURE_IMAGE,
  234.                          ctypes.byref(cam_path),
  235.                          self.context) != GP_OK:
  236.             print "Capture failed"
  237.             self.working = False
  238.         else:
  239.             print "Captured %s in %s" % (cam_path.name, cam_path.folder)
  240.             self.download(cam_path)
  241.  
  242.  
  243.     def check_for_event(self):
  244.     event = CameraEventType()
  245.         data_ptr = ctypes.c_void_p()
  246.  
  247.         ret = self.gp.gp_camera_wait_for_event(self.camera,
  248.                          1000, # wait in ms
  249.                          ctypes.byref(event),
  250.                          ctypes.byref(data_ptr),
  251.                          self.context)
  252.         if ret != GP_OK:
  253.             print "wait for event failed: %d", ret
  254.             self.working = False
  255.         else:
  256.             if event.value == GP_EVENT_FILE_ADDED:
  257.                 cam_path = ctypes.cast(data_ptr, ctypes.POINTER(CameraFilePath)).contents
  258.                 self.download(cam_path)
  259.  
  260.     def run(self):
  261.         # Make temporary directory
  262.         self.directory = tempfile.mkdtemp()
  263.  
  264.         #clock = pygame.time.Clock()
  265.  
  266.         # Load library
  267.         self.gp = ctypes.CDLL(find_library('gphoto2'))
  268.  
  269.         self.init_camera()
  270.  
  271.         self.todo.acquire()
  272.         while self.running:
  273.             while not self.working and self.running:
  274.                 self.todo.release()
  275.                 self.status('*** Camera Error ***')
  276.                 time.sleep(1)
  277.                 self.release_camera()
  278.                 self.init_camera()
  279.                 self.todo.acquire()
  280.  
  281.             self.status('ready.')
  282.  
  283.             if not self.queued_shots and self.manual:
  284.                 self.todo.wait()
  285.  
  286.             while self.queued_shots and self.running:
  287.                 self.todo.release()
  288.                 self.capture()
  289.                 self.todo.acquire()
  290.                 self.queued_shots -= 1
  291.  
  292.             if not self.manual and self.running:
  293.                 self.todo.release()
  294.                 self.check_for_event()
  295.                 self.todo.acquire()
  296.  
  297.             #if clock.tick_busy_loop() < 100:
  298.             #    print "Loop has run too fast, reinitializing camera"
  299.             #    self.working = False
  300.         self.todo.release()
  301.  
  302.         self.release_camera()
  303.         os.rmdir(self.directory)
  304.  
  305.  
  306. ################
  307. #
  308. # Non camera relevant interface
  309. #
  310. ################
  311.  
  312.  
  313. class Actor(object):
  314.     def on_terminate(self):
  315.         pass
  316.  
  317.     def on_draw(self):
  318.         pass
  319.  
  320.     def on_event(self, event):
  321.         if event.type == pygame.KEYDOWN:
  322.             return self.on_keydown(event)
  323.         elif event.type == pygame.USEREVENT:
  324.             return self.on_userevent(event)
  325.  
  326.     def on_keydown(self, event):
  327.         if self.keymap is not None and self.keymap.has_key(event.key):
  328.             self.keymap[event.key](self)
  329.             return True
  330.         return False
  331.  
  332.     def on_userevent(self, event):
  333.         pass
  334.  
  335. #######
  336. #
  337. # Abstract Box, and specialized Textbox, FilenameBox and HelpBox
  338. #
  339. #######
  340.  
  341. class Box(Actor):
  342.     BorderColor = pygame.Color('white')
  343.     FillColor = pygame.Color('lightgreen')
  344.     FontColor = pygame.Color('black')
  345.  
  346.     def __init__ (self, textlines, size=None, center=False, fontsize=18):
  347.         self.textlines = textlines
  348.         self.Font = pygame.font.SysFont('Arial,Sans', fontsize)
  349.  
  350.         if size is None:
  351.             size = (App.getInstance().screen.get_width()/2 + TEXT_PADDING,
  352.                     len(self.textlines) * self.Font.get_linesize() + TEXT_PADDING)
  353.         self.size = size
  354.         self.center = center
  355.  
  356.         App.getInstance().push(self)
  357.  
  358.     def on_draw (self):
  359.         screen = App.getInstance().screen
  360.         screen_rect = screen.get_rect()
  361.        
  362.         surface = pygame.Surface(self.size)
  363.         rect = surface.get_rect()
  364.         surface.fill(self.BorderColor)
  365.         surface.fill(self.FillColor, rect.inflate((-4,-4)))
  366.         surface.set_alpha(200)
  367.  
  368.         rect.center = screen_rect.center
  369.         screen.blit(surface, rect)
  370.  
  371.         height = len(self.textlines) * self.Font.get_linesize()
  372.         top = rect.y + (rect.h - height)/2
  373.         left = rect.x + TEXT_PADDING/2
  374.         for line in self.textlines:
  375.             text = self.Font.render(line, True, self.FontColor)
  376.             if self.center: left = rect.x + (rect.w - text.get_width())/2
  377.             screen.blit(text, (left, top))
  378.             top += self.Font.get_linesize()
  379.  
  380.  
  381. class TextBox(Box):
  382.     def __init__(self, lines=[u''], index=None):
  383.         if index is None: index=len(lines)-1
  384.         self.index = index
  385.         Box.__init__(self, lines, center=True)
  386.  
  387.     def on_keydown(self, event):
  388.         if event.key == pygame.K_BACKSPACE:
  389.             self.textlines[self.index] = self.textlines[self.index][:-1]
  390.         elif event.key == pygame.K_RETURN:
  391.             App.getInstance().pop()
  392.             self.on_return(self.textlines[self.index], pygame.key.get_mods() & pygame.KMOD_CTRL)
  393.         elif event.key == pygame.K_ESCAPE:
  394.             App.getInstance().pop()
  395.         elif event.key <= 127: self.textlines[self.index] += event.unicode
  396.         else: return False
  397.         return True
  398.  
  399.     def on_return(self, text):
  400.         pass
  401.  
  402. class FilenameBox(TextBox):
  403.     def __init__(self, filename='', msg=None):
  404.         TextBox.__init__(self, [u'Name? (Abbrechen mit ESCAPE, Bestรคtigen mit ENTER)',
  405.                                 filename] + (msg and [msg] or []), 1)
  406.  
  407.     def on_return(self, text, overwrite=False):
  408.         App.getInstance().dispatch('save', (text, overwrite))
  409.  
  410. #######
  411. #
  412. #  Help window
  413. #
  414. ######
  415.  
  416. class HelpBox (Box):
  417.     keymap = { pygame.K_h: lambda s: App.getInstance().pop() }
  418.  
  419.     def __init__ (self):
  420.         Box.__init__ (self, [u'*** Hilfe ***',
  421.                              u'',
  422.                              u'Bilder auswรคhlen mit den Ziffern 1-9',
  423.                              u'Auswahl vergrรถรŸern mit ENTER',
  424.                              u'Alle Bilder anzeigen mit ESCAPE',
  425.                              u'Ausgewรคhlte Bilder lรถschen mit BACKSPACE (Rรผckstelltaste)',
  426.                              u'Angezeigte Bilder speichern mit S wie save (dann Namen eingeben und ENTER)',
  427.                              u'',
  428.                              u'Zwischen manuellem und automatischem Modus wechseln mit M',
  429.                              u'(Im manuellen Modus kann nicht an der Kamera ausgelรถst werden,',
  430.                              u' dafรผr funktioniert der Auslรถser mit LEERTASTE schneller)',
  431.                              u'',
  432.                              u'Hilfetext ein-/ausblenden mit H wie help',
  433.                              u'annYmage verlassen mit Q wie quit'], fontsize=16)
  434.  
  435.  
  436. class Dialog(Box):
  437.     keymap = {pygame.K_ESCAPE: lambda s: App.getInstance().pop() }
  438.    
  439.     def __init__(self,str):
  440.         Box.__init__(self, [str])
  441.  
  442. #######
  443. #
  444. #  A single Image
  445. #
  446. ######
  447.  
  448. class Image:
  449.     def __init__(self, fn):
  450.         self.filename = fn
  451.  
  452.         im = pygame.image.load(fn)
  453.         size = im.get_size()
  454.         if size > (SCREEN_WIDTH, SCREEN_HEIGHT):
  455.             w = SCREEN_HEIGHT * size[0]/size[1]
  456.             if w > SCREEN_WIDTH: size = (w, SCREEN_HEIGHT)
  457.             else: size = (SCREEN_WIDTH, SCREEN_WIDTH * size[1]/size[0])
  458.             im = pygame.transform.smoothscale(im, size)
  459.            
  460.         self.surface = im.convert()
  461.         self.shown = True
  462.         self.selected = False
  463.  
  464.     def is_shown(self):    return self.shown
  465.     def is_selected(self): return self.selected
  466.     def get_size(self):    return self.surface.get_size()
  467.  
  468.     def show(self, to=None):
  469.         if to is None: self.shown = not self.shown
  470.         else:          self.shown = to
  471.  
  472.     def select(self, to=None):
  473.         if to is None: self.selected = not self.selected
  474.         else:          self.selected = to
  475.         self.surface.set_alpha(self.selected and 100 or None)
  476.  
  477.     def draw(self, screen, dest_rect, no):
  478.         if self.selected:
  479.             screen.fill(Image.SelectionColor, dest_rect.inflate(6,6))
  480.             screen.fill(0, dest_rect)
  481.         screen.blit(pygame.transform.smoothscale(self.surface, dest_rect.size), dest_rect)
  482.  
  483.         text = Image.Font.render("%d" % no, True, Image.TextColor)
  484.         text_rect = text.get_rect()
  485.         text_rect.midtop = dest_rect.midbottom
  486.         text_rect.move_ip(0,3)
  487.         screen.blit(text, text_rect)
  488.  
  489. Image.SelectionColor = pygame.Color('yellow')
  490. Image.TextColor      = pygame.Color('white')
  491. Image.Font = pygame.font.SysFont('Arial,Sans', 18)
  492.  
  493.  
  494. class ImageViewer(Actor):
  495.  
  496.     def __init__ (self, directory, provider):
  497.         self.directory = directory
  498.         self.provider = provider
  499.  
  500.         self.images = []
  501.         self.status = { 'provider': '', 'mode': 'mode: automatic', 'shown': '' }
  502.         self.update_status()
  503.  
  504.         App.getInstance().push(self)
  505.  
  506.  
  507.     keymap = {
  508.         pygame.K_1: lambda s: s.select(0),
  509.         pygame.K_2: lambda s: s.select(1),
  510.         pygame.K_3: lambda s: s.select(2),
  511.         pygame.K_4: lambda s: s.select(3),
  512.         pygame.K_5: lambda s: s.select(4),
  513.         pygame.K_6: lambda s: s.select(5),
  514.         pygame.K_7: lambda s: s.select(6),
  515.         pygame.K_8: lambda s: s.select(7),
  516.         pygame.K_9: lambda s: s.select(8),
  517.         pygame.K_0: lambda s: s.select(9),
  518.  
  519.         pygame.K_RETURN: lambda s: s.show_selected(),
  520.         pygame.K_ESCAPE: lambda s: s.show_all(),
  521.  
  522.         pygame.K_BACKSPACE: lambda s: s.del_selected(),
  523.         pygame.K_s: lambda s: s.choose_shown(),
  524.  
  525.         pygame.K_UP: lambda s: s.show_more(+1),
  526.         pygame.K_DOWN: lambda s: s.show_more(-1),
  527.  
  528.         pygame.K_m: lambda s: s.switch_mode(),
  529.         pygame.K_SPACE: lambda s: s.provider.shoot(),
  530.  
  531.         pygame.K_h: lambda s: s.show_help(),
  532.         pygame.K_q: lambda s: App.getInstance().terminate()
  533.         }
  534.  
  535.     ####
  536.     # Keymap functions
  537.     ####
  538.  
  539.     def select (self, no):
  540.         shown = self.get_shown()
  541.         if (no >= len(shown)):
  542.             self.show_more(no-len(shown)+1)
  543.         else:
  544.             shown[no].select()
  545.  
  546.     def show_selected (self):
  547.         for img in self.images:
  548.             img.show(img.is_selected())
  549.             img.select(False)
  550.         self.update_status()
  551.  
  552.     def show_all (self):
  553.         for img in self.images:
  554.             img.show(True)
  555.         self.update_status()
  556.  
  557.     def del_selected (self):
  558.         images = []
  559.         for img in self.images:
  560.             if img.is_selected(): os.unlink(img.filename)
  561.             else:                 images.append(img)
  562.         self.images = images
  563.         self.update_status()
  564.  
  565.     def choose_shown (self):
  566.         '''Launches FilenameBox. The entered information returns by userevent 'save'.'''
  567.         FilenameBox()
  568.  
  569.     def show_more (self, count):
  570.         print "show_more(%d)" % count
  571.         show = bool(count > 0)
  572.         count = abs(count)
  573.         for img in self.images:
  574.             if not count > 0: break
  575.             if img.is_shown() != show:
  576.                 img.show()
  577.                 count -= 1
  578.         self.update_status()
  579.  
  580.     def switch_mode (self):
  581.         self.status['mode'] = 'switching mode ...'
  582.         self.provider.mode()
  583.  
  584.     def show_help (self):
  585.         HelpBox()
  586.  
  587.  
  588.     ####
  589.     # Small helper functions
  590.     ####
  591.  
  592.     def update_status (self):
  593.         self.status['shown'] = ("%d / %d images are shown" % (len(self.get_shown()),len(self.images)))
  594.  
  595.     def load (self, filenames):
  596.         for fn in filenames:
  597.             self.images.append(Image(fn))
  598.         self.update_status()
  599.  
  600.     def del_all (self):
  601.         for img in self.images:
  602.             try:
  603.                 os.unlink(img.filename)
  604.             except OSError: pass
  605.         self.images = []
  606.         self.update_status()
  607.  
  608.     def get_shown(self):
  609.         return filter(lambda x: x.is_shown(), self.images)
  610.  
  611.  
  612.     ####
  613.     # Event functions
  614.     ####
  615.  
  616.     def on_userevent (self, event):
  617.         if (event.name == 'save'):
  618.             chosen = self.get_shown()
  619.             selected = filter(lambda x: x.is_selected(), chosen)
  620.             if selected: chosen = selected
  621.  
  622.             (filename, overwrite) = event.data
  623.             index = 1
  624.             for img in chosen:
  625.                 suffix = img.filename.rsplit('.',1)[1]
  626.                 fn = os.path.join(self.directory, "%s_%d.%s" % (filename, index, suffix))
  627.                 print "Moving %s to %s" % (img.filename, fn)
  628.  
  629.                 if (not overwrite and os.path.exists(fn)):
  630.                     FilenameBox(filename, u'Datei mit diesem Namen existiert bereits. รœberschreiben mit CTRL-ENTER.')
  631.                     return True
  632.  
  633.                 shutil.move(img.filename, fn)
  634.                 self.images.remove(img)
  635.  
  636.                 index += 1
  637.             self.del_all()
  638.         elif (event.name == 'load'):
  639.             if not isinstance(event.data, (types.TupleType, types.ListType)):
  640.                 event.data = [event.data]
  641.             self.load(event.data)
  642.         elif (event.name == 'status'):
  643.             self.status['provider'] = event.data
  644.         elif (event.name == 'mode'):
  645.             self.status['mode'] = "mode: %s" % event.data
  646.         else:
  647.             return False
  648.         return True
  649.  
  650.     def on_terminate (self):
  651.         self.del_all()
  652.  
  653.     def find_best_rect (self, count):
  654.         screen_size = App.getInstance().screen.get_size()
  655.         image_rect = pygame.Rect((0,0), self.images[0].get_size())
  656.         best_rect = pygame.Rect(0,0,0,0)
  657.         best_cols = 0
  658.  
  659.         for cols in range(1,count+1):
  660.             rows = math.ceil(float(count) / cols)
  661.             box_rect = pygame.Rect((0,0), (screen_size[0]/cols - DIST, (screen_size[1] - STATUS_H)/rows - CAPTION_H - DIST))
  662.  
  663.             pro_rect = image_rect.fit(box_rect)
  664.             if pro_rect.size > best_rect.size:
  665.                 best_rect = pro_rect
  666.                 best_cols = cols
  667.  
  668.         return (best_cols, best_rect)
  669.  
  670.     def on_draw (self):
  671.         screen = App.getInstance().screen
  672.  
  673.         # Drawing status
  674.         tab = (SCREEN_WIDTH - DIST)/len(self.status)
  675.         top = SCREEN_HEIGHT - (STATUS_H + ImageViewer.Font.get_linesize())/2
  676.         left = DIST/2
  677.         for v in self.status.itervalues():
  678.             screen.blit(ImageViewer.Font.render(v, True, ImageViewer.Color), (left, top))
  679.             left += tab
  680.  
  681.         if len(self.images) == 0: return
  682.  
  683.         shown = self.get_shown()
  684.         (best_cols, best_rect) = self.find_best_rect(len(shown))
  685.         no = 1
  686.         rect = best_rect.move(DIST/2, DIST/2)
  687.  
  688.         for img in shown:
  689.             img.draw(screen, rect, no)
  690.             rect.move_ip((best_rect.w + DIST + 2*best_rect.x, 0))
  691.             if (no % best_cols == 0):
  692.                 rect.move_ip((- best_cols * (best_rect.w + DIST + 2*best_rect.x), best_rect.h + CAPTION_H + DIST + 2*best_rect.y))
  693.             no += 1
  694.  
  695.  
  696. ImageViewer.Color = pygame.Color('white')
  697. ImageViewer.Font = pygame.font.SysFont('Arial,Sans', 12)
  698.  
  699.  
  700. class Singleton(object):
  701.     '''Implement Pattern: SINGLETON'''
  702.     __lockObj = threading.RLock() # lock object
  703.     __instance = None # the unique instance
  704.  
  705.     def __new__(cls, *args, **kargs):
  706.         return cls.getInstance(*args, **kargs)
  707.  
  708.     def init(self):
  709.         pass
  710.  
  711.     @classmethod
  712.     def getInstance(cls, *args, **kargs):
  713.         '''Static method to have a reference to **THE UNIQUE** instance'''
  714.         # Critical section start
  715.         cls.__lockObj.acquire()
  716.         try:
  717.             if cls.__instance is None:
  718.                 # (Some exception may be thrown...)
  719.                 # Initialize **the unique** instance
  720.                 cls.__instance = object.__new__(cls)
  721.                 cls.__instance.init(*args, **kargs)
  722.  
  723.                 #'''Initialize object **here**, as you would do in __init__()...'''
  724.         finally:
  725.             # Exit from critical section whatever happens
  726.             cls.__lockObj.release()
  727.  
  728.         # Critical section end
  729.         return cls.__instance
  730.  
  731.  
  732. class App (Singleton, Actor):
  733.  
  734.     def init(self, directory, provider):
  735.         self.stack = [self]
  736.         self.provider = provider
  737.  
  738.         pygame.display.set_icon(pygame.image.load('annYmage.gif'))
  739.         self.screen = pygame.display.set_mode( (SCREEN_WIDTH, SCREEN_HEIGHT) )
  740.         pygame.display.set_caption('annYmage')
  741.         ImageViewer(directory, provider)
  742.  
  743.     def dispatch(self, name, data):
  744.         pygame.event.post(pygame.event.Event(pygame.USEREVENT, {'name': name, 'data': data}))
  745.  
  746.     def push(self, instance):
  747.         self.stack.insert(0, instance)
  748.  
  749.     def pop(self):
  750.         return self.stack.pop(0)
  751.  
  752.     def on_event(self, event):
  753.         if event.type == pygame.QUIT:
  754.             App.getInstance().terminate()
  755.  
  756.     def handleEvent(self, event):
  757.         for layer in self.stack:
  758.             if (layer.on_event(event)): break
  759.  
  760.     def run(self):
  761.         self.stop = False
  762.         while not self.stop:
  763.             self.handleEvent(pygame.event.wait())
  764.             for event in pygame.event.get():
  765.                 self.handleEvent(event)
  766.  
  767.             #print "Will draw on ", self.stack[0]
  768.             self.screen.fill(0)
  769.             for layer in reversed(self.stack):
  770.                 layer.on_draw()
  771.             pygame.display.flip();
  772.  
  773.     def terminate(self):
  774.         self.stop = True
  775.         for layer in self.stack:
  776.             layer.on_terminate()
  777.         self.provider.terminate()
  778.  
  779. def main(argv=[]):
  780.     directory = (len(argv) > 1) and argv[1] or SAVE_DIRECTORY
  781.  
  782.     #provider = ImageProviderDirectory('test')
  783.     provider = ImageProviderCamera()
  784.     provider.start()
  785.     try:
  786.         App(directory, provider).run()
  787.     except Exception as e:
  788.         provider.terminate()
  789.         raise e
  790.  
  791.  
  792. if __name__ == '__main__':
  793.     main(sys.argv)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement