def directSnapshotTask(self, parameters, preview, *args): # running on a Raspberry Pi #print parameters, preview, args try: if self.directCamera == None: try: print "Using picam directly" self.usingDirectPicam = True from piKamPicamServer import PiKamPicamServerProtocal self.directCamera = PiKamPicamServerProtocal() except: self.usingDirectPicam = False print "Using raspistill directly" from piKamServer import PiKamServerProtocal self.directCamera = PiKamServerProtocal() imageFilename, image, imageType, replyMessageType = self.directCamera.takePhoto(parameters) # Schedule to show image in main event thread # Do slow as much image manipulation as possible in this thread to prevent the GUI blocking if preview: # raspistill will have already saved the image, just display it. from functools import partial #self.displayPreview(textureFromPyImage(borderPyImage(image))) Clock.schedule_once(partial(self.displayPreview, borderPyImage(image))) else: if self.usingDirectPicam: # Need to write the image out. image.save(imageFilename, imageType) from functools import partial Clock.schedule_once(partial(self.displayImage, downsizePyImage(image))) except Exception, error: print str(error) self.displayError('No remote hostname set, you need to be running this on a Raspberry Pi. ' + str(error) )
def directSnapshotTask(self, parameters, preview, *args): # running on a Raspberry Pi #print parameters, preview, args try: if self.directCamera == None: try: print "Using picam directly" self.usingDirectPicam = True from piKamPicamServer import PiKamPicamServerProtocal self.directCamera = PiKamPicamServerProtocal() except: self.usingDirectPicam = False print "Using raspistill directly" from piKamServer import PiKamServerProtocal self.directCamera = PiKamServerProtocal() imageFilename, image, imageType, replyMessageType = self.directCamera.takePhoto( parameters) # Schedule to show image in main event thread # Do slow as much image manipulation as possible in this thread to prevent the GUI blocking if preview: # raspistill will have already saved the image, just display it. from functools import partial #self.displayPreview(textureFromPyImage(borderPyImage(image))) Clock.schedule_once( partial(self.displayPreview, borderPyImage(image))) else: if self.usingDirectPicam: # Need to write the image out. image.save(imageFilename, imageType) from functools import partial Clock.schedule_once( partial(self.displayImage, downsizePyImage(image))) except Exception, error: print str(error) self.displayError( 'No remote hostname set, you need to be running this on a Raspberry Pi. ' + str(error))
class PiKamApp(App): chdkConnection = None model = PiKamModel() ndFilter = False exposureComp = 0 # TODO previewImage = None waitingForImage = False previewTask = None screenMgr = None directCamera = None mark = None mark2 = None runningOnPi = False usingDirectPicam = False def build(self): self.runningOnPi = self.config.get('Server', 'hostname').strip() == '' self.screenMgr = ScreenManager() horzScreen = PiKamHorizontalScreen(name='horz') vertScreen = PiKamVerticalScreen(name='vert') x,y = Window.system_size detectedLandscape = x > y and False # Not working on Android - darn! for screenWidget in ( horzScreen, vertScreen ) if (detectedLandscape or self.config.get('Misc', 'horizontalLayout') == '1') else ( vertScreen, horzScreen ): self.screenMgr.add_widget(screenWidget) if self.config.get('Misc', 'splash') != '0' and os.path.exists('piKamSplash.jpg'): self.displayImage(PyImage.open('piKamSplash.jpg')) self.reconnect() print vars(self) if self.runningOnPi: Window.bind(on_motion=self.plot_click_pos) Clock.schedule_interval(self.plot_motion, .5) #Window.rotation = Window.rotation + 90 Window.on_rotate(self.rotate) return self.screenMgr def plot_click_pos(self, x, etype, motionevent): if self.runningOnPi: # Cannot see where mouse is on Raspberry Pi Kivy - provide some # indicator. if self.mark2: self.screenMgr.current_screen.canvas.remove(self.mark2) self.mark2 = Rectangle(pos=motionevent.pos, size=(5, 5)) self.screenMgr.current_screen.canvas.add(self.mark2) def plot_motion(self, *args): # Cannot see where mouse is on Raspberry Pi Kivy - provide some # indicator. if self.mark: if self.mark.pos == Window.mouse_pos: return self.screenMgr.current_screen.canvas.remove(self.mark) #print 'mouse at', Window.mouse_pos self.mark = Rectangle(pos=Window.mouse_pos, size=(5, 5)) self.screenMgr.current_screen.canvas.add(self.mark) def rotate(self, screenName=None): print "rotate" if self.screenMgr.current == 'horz': self.screenMgr.current = 'vert' else: self.screenMgr.current = 'horz' #Window.rotation = Window.rotation + 90 def build_config(self, config): config.setdefaults('Server', {'hostname': '', 'port': '8000'}) config.setdefaults('Camera', {'encoding': 'jpg', 'quality': 0, 'sharpness': 0, 'hflip': 0, 'vflip': 0}) config.setdefaults('Misc', {'carousel': 1, 'splash': 1, 'preview': 1, 'horizontalLayout':1, 'numSlides': 10, 'previewQuality':5, 'previewRefresh':1.5}) def build_settings(self, settings): # Javascript Object Notation settings.add_json_panel('PiKam App', self.config, data=SETTINGS_JSON_DATA) def on_config_change(self, config, section, key, value): if config is self.config: if section == 'Server': self.reconnect() if key == 'preview' or key == 'previewQuality' or key == 'previewRefresh': self.disablePreview() self.enablePreview() if key == 'horizontalLayout': self.screenMgr.current = 'horz' if self.config.get('Misc', 'horizontalLayout') != '0' else 'vert' # Force image to be regenerated and reparented in new widget hierarchy self.previewImage = None def displayInfo(self, message, title='Info'): popContent = BoxLayout(orientation='vertical') popContent.add_widget(Label(text=message)) popup = Popup(title=title, content=popContent, text_size=(len(message), None), size_hint=(.8, .33)) popContent.add_widget(Button(text='Close', size_hint=(1,.33), on_press=popup.dismiss)) popup.open() def currentTop(self): return self.screenMgr.current_screen def displayError(self, message, title='Error'): self.displayInfo(message, title) def displayProgress(self, value): # If zero then we don't want progress for this op. if self.currentTop().downloadProgress.value > 0: self.currentTop().downloadProgress.value += value def displayBusyWaiting(self, dt=None): if dt == None: #print "schedule" self.busyWaiting = True Clock.schedule_interval(self.displayBusyWaiting, 1 / 10.) return # Fake progress updates until the real updates happen if self.busyWaiting: self.currentTop().downloadProgress.value += 30000 return True else: # If the values differ, then #print "stop" return False def stopBusyWaiting(self): self.busyWaiting = False self.currentTop().downloadProgress.value = 0 def displayImage(self, pyImg, *args): try: useCarousel = self.config.get('Misc', 'carousel') != '0' # Load Kivy Image from PyImage without going to disk image = Image(texture=textureFromPyImage(pyImg)) if useCarousel: self.currentTop().imageCarousel.add_widget(image) # Set the carousel to display the new image (could exhaust memory - perhaps only display last N) self.currentTop().imageCarousel.index = len(self.currentTop().imageCarousel.slides) - 1 numSlides = int(self.config.get('Misc', 'numSlides')) if len(self.currentTop().imageCarousel.slides) > numSlides: self.currentTop().imageCarousel.remove_widget(self.currentTop().imageCarousel.slides[0]) else: self.currentTop().imageLayout.clear_widgets() self.currentTop().imageLayout.add_widget(image) finally: self.stopBusyWaiting() self.waitingForImage = False def displayPreview(self, pyImg, *args): try: useCarousel = self.config.get('Misc', 'carousel') != '0' if self.previewImage: self.previewImage.texture = textureFromPyImage(pyImg) if useCarousel: # Shuffle to end oldIndex = self.currentTop().imageCarousel.index self.currentTop().imageCarousel.remove_widget(self.previewImage) self.currentTop().imageCarousel.add_widget(self.previewImage) if oldIndex == len(self.currentTop().imageCarousel.slides) - 1: self.currentTop().imageCarousel.index = len(self.currentTop().imageCarousel.slides) - 1 else: self.previewImage = Image(texture=textureFromPyImage(pyImg)) self.previewImage.nocache = True if useCarousel: oldIndex = self.currentTop().imageCarousel.index self.currentTop().imageCarousel.add_widget(self.previewImage) # Set the carousel to display the new image (could exhaust memory - perhaps only display last N) if oldIndex == len(self.currentTop().imageCarousel.slides) - 1: self.currentTop().imageCarousel.index = len(self.currentTop().imageCarousel.slides) - 1 else: self.currentTop().imageLayout.clear_widgets() self.currentTop().imageLayout.add_widget(self.previewImage) finally: self.waitingForImage = False def on_connection(self, connection): self.displayInfo('Connected succesfully!') self.chdkConnection = connection self.prepareCamera() self.enablePreview() def on_start(self): if self.runningOnPi: # On a Raspberry Pi - start preview - if remote it # will be started by on_connection self.enablePreview() def on_pause(self): #reactor._mainLoopShutdown() self.disablePreview() return True def on_resume(self): self.reconnect() return True def sendRemoteCommand(self, message): if self.chdkConnection: # Compose Netstring format message and send it (might be able to call sendString but is undocumented) self.chdkConnection.write(str(len(message)) + ':' + message + ',') else: self.displayError('No connection to server') def processRemoteResponse(self, message): # Turn the response string back nto a dictionary and see what it is result = cPickle.loads(message) if result['type'] == 'image': # Save the image and add an internal copy to the GUI carousel. filename = result['name'] with open(filename, 'wb') as imageFile: imageFile.write(result['data']) self.displayImage(downsizePyImage(pyImageFromStr(result['data']))) elif result['type'] == 'preview': self.displayPreview(borderPyImage(pyImageFromStr(result['data']))) elif result['type'] == 'error': self.displayError(result['message']) else: self.displayError('Unexpected kind of message.') def takeSnapshot(self, preview = False): if self.waitingForImage and preview: print 'already waiting', self.waitingForImage return self.waitingForImage = True self.model.setConfig(self.config) command = {} command['cmd'] = 'shoot' args = self.model.toRequest() if preview: args.height = 480 args.width = 640 args.encoding = 'jpg' args.quality = self.config.get('Misc', 'previewQuality') args.replyMessageType = 'preview' if self.runningOnPi: # On a Raspberry Pi already self.directSnapshot(args, preview) else: command['args'] = args # Turn the request into a string so it can be sent in Netstring format self.sendRemoteCommand(cPickle.dumps(command)) if not preview: self.displayBusyWaiting() def prepareCamera(self): command = {'cmd': 'prepareCamera'} args = self.model.toRequest() command['args'] = args self.sendRemoteCommand(cPickle.dumps(command)) pass def reconnect(self): if self.runningOnPi: return hostname = self.config.get('Server', 'hostname') port = self.config.getint('Server', 'port') reactor.connectTCP(hostname, port, PiKamClientFactory(self)) def requestPreview(self): #print 'pv' useCarousel = self.config.get('Misc', 'carousel') != '0' numSlides = len(self.currentTop().imageCarousel.slides) if useCarousel and numSlides != 0 and self.currentTop().imageCarousel.index != numSlides - 1: # Not looking at preview - don't refresh it return self.takeSnapshot(preview=True) def enablePreview(self): if self.config.get('Misc', 'preview') == '0' or self.previewTask: return print 'enablePreview' self.waitingForImage = False self.previewTask = task.LoopingCall(self.requestPreview) refresh = float(self.config.get('Misc', 'previewRefresh')) self.previewTask.start(refresh) def directSnapshot(self, parameters, preview): if not preview: self.displayBusyWaiting() from threading import Thread # Perform in background - allow GUI to continue responding thread = Thread(target=self.directSnapshotTask, args=(parameters, preview)) thread.start() def directSnapshotTask(self, parameters, preview, *args): # running on a Raspberry Pi #print parameters, preview, args try: if self.directCamera == None: try: print "Using picam directly" self.usingDirectPicam = True from piKamPicamServer import PiKamPicamServerProtocal self.directCamera = PiKamPicamServerProtocal() except: self.usingDirectPicam = False print "Using raspistill directly" from piKamServer import PiKamServerProtocal self.directCamera = PiKamServerProtocal() imageFilename, image, imageType, replyMessageType = self.directCamera.takePhoto(parameters) # Schedule to show image in main event thread # Do slow as much image manipulation as possible in this thread to prevent the GUI blocking if preview: # raspistill will have already saved the image, just display it. from functools import partial #self.displayPreview(textureFromPyImage(borderPyImage(image))) Clock.schedule_once(partial(self.displayPreview, borderPyImage(image))) else: if self.usingDirectPicam: # Need to write the image out. image.save(imageFilename, imageType) from functools import partial Clock.schedule_once(partial(self.displayImage, downsizePyImage(image))) except Exception, error: print str(error) self.displayError('No remote hostname set, you need to be running this on a Raspberry Pi. ' + str(error) )
class PiKamApp(App): chdkConnection = None model = PiKamModel() ndFilter = False exposureComp = 0 # TODO previewImage = None waitingForImage = False previewTask = None screenMgr = None directCamera = None mark = None mark2 = None runningOnPi = False usingDirectPicam = False def build(self): self.runningOnPi = self.config.get('Server', 'hostname').strip() == '' self.screenMgr = ScreenManager() horzScreen = PiKamHorizontalScreen(name='horz') vertScreen = PiKamVerticalScreen(name='vert') x, y = Window.system_size detectedLandscape = x > y and False # Not working on Android - darn! for screenWidget in (horzScreen, vertScreen) if ( detectedLandscape or self.config.get( 'Misc', 'horizontalLayout') == '1') else (vertScreen, horzScreen): self.screenMgr.add_widget(screenWidget) if self.config.get( 'Misc', 'splash') != '0' and os.path.exists('piKamSplash.jpg'): self.displayImage(PyImage.open('piKamSplash.jpg')) self.reconnect() print vars(self) if self.runningOnPi: Window.bind(on_motion=self.plot_click_pos) Clock.schedule_interval(self.plot_motion, .5) #Window.rotation = Window.rotation + 90 Window.on_rotate(self.rotate) return self.screenMgr def plot_click_pos(self, x, etype, motionevent): if self.runningOnPi: # Cannot see where mouse is on Raspberry Pi Kivy - provide some # indicator. if self.mark2: self.screenMgr.current_screen.canvas.remove(self.mark2) self.mark2 = Rectangle(pos=motionevent.pos, size=(5, 5)) self.screenMgr.current_screen.canvas.add(self.mark2) def plot_motion(self, *args): # Cannot see where mouse is on Raspberry Pi Kivy - provide some # indicator. if self.mark: if self.mark.pos == Window.mouse_pos: return self.screenMgr.current_screen.canvas.remove(self.mark) #print 'mouse at', Window.mouse_pos self.mark = Rectangle(pos=Window.mouse_pos, size=(5, 5)) self.screenMgr.current_screen.canvas.add(self.mark) def rotate(self, screenName=None): print "rotate" if self.screenMgr.current == 'horz': self.screenMgr.current = 'vert' else: self.screenMgr.current = 'horz' #Window.rotation = Window.rotation + 90 def build_config(self, config): config.setdefaults('Server', {'hostname': '', 'port': '8000'}) config.setdefaults( 'Camera', { 'encoding': 'jpg', 'quality': 0, 'sharpness': 0, 'hflip': 0, 'vflip': 0 }) config.setdefaults( 'Misc', { 'carousel': 1, 'splash': 1, 'preview': 1, 'horizontalLayout': 1, 'numSlides': 10, 'previewQuality': 5, 'previewRefresh': 1.5 }) def build_settings(self, settings): # Javascript Object Notation settings.add_json_panel('PiKam App', self.config, data=SETTINGS_JSON_DATA) def on_config_change(self, config, section, key, value): if config is self.config: if section == 'Server': self.reconnect() if key == 'preview' or key == 'previewQuality' or key == 'previewRefresh': self.disablePreview() self.enablePreview() if key == 'horizontalLayout': self.screenMgr.current = 'horz' if self.config.get( 'Misc', 'horizontalLayout') != '0' else 'vert' # Force image to be regenerated and reparented in new widget hierarchy self.previewImage = None def displayInfo(self, message, title='Info'): popContent = BoxLayout(orientation='vertical') popContent.add_widget(Label(text=message)) popup = Popup(title=title, content=popContent, text_size=(len(message), None), size_hint=(.8, .33)) popContent.add_widget( Button(text='Close', size_hint=(1, .33), on_press=popup.dismiss)) popup.open() def currentTop(self): return self.screenMgr.current_screen def displayError(self, message, title='Error'): self.displayInfo(message, title) def displayProgress(self, value): # If zero then we don't want progress for this op. if self.currentTop().downloadProgress.value > 0: self.currentTop().downloadProgress.value += value def displayBusyWaiting(self, dt=None): if dt == None: #print "schedule" self.busyWaiting = True Clock.schedule_interval(self.displayBusyWaiting, 1 / 10.) return # Fake progress updates until the real updates happen if self.busyWaiting: self.currentTop().downloadProgress.value += 30000 return True else: # If the values differ, then #print "stop" return False def stopBusyWaiting(self): self.busyWaiting = False self.currentTop().downloadProgress.value = 0 def displayImage(self, pyImg, *args): try: useCarousel = self.config.get('Misc', 'carousel') != '0' # Load Kivy Image from PyImage without going to disk image = Image(texture=textureFromPyImage(pyImg)) if useCarousel: self.currentTop().imageCarousel.add_widget(image) # Set the carousel to display the new image (could exhaust memory - perhaps only display last N) self.currentTop().imageCarousel.index = len( self.currentTop().imageCarousel.slides) - 1 numSlides = int(self.config.get('Misc', 'numSlides')) if len(self.currentTop().imageCarousel.slides) > numSlides: self.currentTop().imageCarousel.remove_widget( self.currentTop().imageCarousel.slides[0]) else: self.currentTop().imageLayout.clear_widgets() self.currentTop().imageLayout.add_widget(image) finally: self.stopBusyWaiting() self.waitingForImage = False def displayPreview(self, pyImg, *args): try: useCarousel = self.config.get('Misc', 'carousel') != '0' if self.previewImage: self.previewImage.texture = textureFromPyImage(pyImg) if useCarousel: # Shuffle to end oldIndex = self.currentTop().imageCarousel.index self.currentTop().imageCarousel.remove_widget( self.previewImage) self.currentTop().imageCarousel.add_widget( self.previewImage) if oldIndex == len( self.currentTop().imageCarousel.slides) - 1: self.currentTop().imageCarousel.index = len( self.currentTop().imageCarousel.slides) - 1 else: self.previewImage = Image(texture=textureFromPyImage(pyImg)) self.previewImage.nocache = True if useCarousel: oldIndex = self.currentTop().imageCarousel.index self.currentTop().imageCarousel.add_widget( self.previewImage) # Set the carousel to display the new image (could exhaust memory - perhaps only display last N) if oldIndex == len( self.currentTop().imageCarousel.slides) - 1: self.currentTop().imageCarousel.index = len( self.currentTop().imageCarousel.slides) - 1 else: self.currentTop().imageLayout.clear_widgets() self.currentTop().imageLayout.add_widget(self.previewImage) finally: self.waitingForImage = False def on_connection(self, connection): self.displayInfo('Connected succesfully!') self.chdkConnection = connection self.prepareCamera() self.enablePreview() def on_start(self): if self.runningOnPi: # On a Raspberry Pi - start preview - if remote it # will be started by on_connection self.enablePreview() def on_pause(self): #reactor._mainLoopShutdown() self.disablePreview() return True def on_resume(self): self.reconnect() return True def sendRemoteCommand(self, message): if self.chdkConnection: # Compose Netstring format message and send it (might be able to call sendString but is undocumented) self.chdkConnection.write(str(len(message)) + ':' + message + ',') else: self.displayError('No connection to server') def processRemoteResponse(self, message): # Turn the response string back nto a dictionary and see what it is result = cPickle.loads(message) if result['type'] == 'image': # Save the image and add an internal copy to the GUI carousel. filename = result['name'] with open(filename, 'wb') as imageFile: imageFile.write(result['data']) self.displayImage(downsizePyImage(pyImageFromStr(result['data']))) elif result['type'] == 'preview': self.displayPreview(borderPyImage(pyImageFromStr(result['data']))) elif result['type'] == 'error': self.displayError(result['message']) else: self.displayError('Unexpected kind of message.') def takeSnapshot(self, preview=False): if self.waitingForImage and preview: print 'already waiting', self.waitingForImage return self.waitingForImage = True self.model.setConfig(self.config) command = {} command['cmd'] = 'shoot' args = self.model.toRequest() if preview: args.height = 480 args.width = 640 args.encoding = 'jpg' args.quality = self.config.get('Misc', 'previewQuality') args.replyMessageType = 'preview' if self.runningOnPi: # On a Raspberry Pi already self.directSnapshot(args, preview) else: command['args'] = args # Turn the request into a string so it can be sent in Netstring format self.sendRemoteCommand(cPickle.dumps(command)) if not preview: self.displayBusyWaiting() def prepareCamera(self): command = {'cmd': 'prepareCamera'} args = self.model.toRequest() command['args'] = args self.sendRemoteCommand(cPickle.dumps(command)) pass def reconnect(self): if self.runningOnPi: return hostname = self.config.get('Server', 'hostname') port = self.config.getint('Server', 'port') reactor.connectTCP(hostname, port, PiKamClientFactory(self)) def requestPreview(self): #print 'pv' useCarousel = self.config.get('Misc', 'carousel') != '0' numSlides = len(self.currentTop().imageCarousel.slides) if useCarousel and numSlides != 0 and self.currentTop( ).imageCarousel.index != numSlides - 1: # Not looking at preview - don't refresh it return self.takeSnapshot(preview=True) def enablePreview(self): if self.config.get('Misc', 'preview') == '0' or self.previewTask: return print 'enablePreview' self.waitingForImage = False self.previewTask = task.LoopingCall(self.requestPreview) refresh = float(self.config.get('Misc', 'previewRefresh')) self.previewTask.start(refresh) def directSnapshot(self, parameters, preview): if not preview: self.displayBusyWaiting() from threading import Thread # Perform in background - allow GUI to continue responding thread = Thread(target=self.directSnapshotTask, args=(parameters, preview)) thread.start() def directSnapshotTask(self, parameters, preview, *args): # running on a Raspberry Pi #print parameters, preview, args try: if self.directCamera == None: try: print "Using picam directly" self.usingDirectPicam = True from piKamPicamServer import PiKamPicamServerProtocal self.directCamera = PiKamPicamServerProtocal() except: self.usingDirectPicam = False print "Using raspistill directly" from piKamServer import PiKamServerProtocal self.directCamera = PiKamServerProtocal() imageFilename, image, imageType, replyMessageType = self.directCamera.takePhoto( parameters) # Schedule to show image in main event thread # Do slow as much image manipulation as possible in this thread to prevent the GUI blocking if preview: # raspistill will have already saved the image, just display it. from functools import partial #self.displayPreview(textureFromPyImage(borderPyImage(image))) Clock.schedule_once( partial(self.displayPreview, borderPyImage(image))) else: if self.usingDirectPicam: # Need to write the image out. image.save(imageFilename, imageType) from functools import partial Clock.schedule_once( partial(self.displayImage, downsizePyImage(image))) except Exception, error: print str(error) self.displayError( 'No remote hostname set, you need to be running this on a Raspberry Pi. ' + str(error))