<commit_before>#!/usr/bin/python
#===========================================================================
# laser_curses.py
#
# Curses interface to drive laser and camera.
#
# 2014-10-11
# Carter Nelson
#===========================================================================

#-------------------------------
# import modules
#-------------------------------
from Adafruit_Raspberry_Pi_Python_Code.Adafruit_PWM_Servo_Driver.Adafruit_PWM_Servo_Driver import PWM
import RPi.GPIO as io
io.setmode(io.BCM)
import time
import curses
#import picamera

#-------------------------------
# setup
#-------------------------------
OE_PIN      = 4          # servo output control
OE_State    = io.HIGH    # LOW=ON  HIGH=OFF
LASER_PIN   = 16         # servo output control
LASER_State = io.LOW     # LOW=OFF  HIGH=ON
laserXChan  = 2          # left/right
laserYChan  = 3          # up/down
cameraXChan = 0          # left/right
cameraYChan = 1          # up/down
servoMin    = 205        # min pulse length out of 4096 (linear range)
servoMax    = 410        # max pulse length out of 4096 (linear range)
#servoMin    = 130        # min pulse length out of 4096 (max range)
#servoMax    = 600        # max pulse length out of 4096 (max range)
servoStep   = 1          # step value for servo moves
laserXVal   = (servoMin+servoMax)/2
laserYVal   = (servoMin+servoMax)/2
cameraXVal  = (servoMin+servoMax)/2
cameraYVal  = (servoMin+servoMax)/2

cameraS1 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
cameraS2 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
cameraS3 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
cameraS4 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )

laserS1 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
laserS2 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
laserS3 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )
laserS4 = ( (servoMin+servoMax)/2, (servoMin+servoMax)/2 )

io.setup(OE_PIN, io.OUT)
io.output(OE_PIN, OE_State)
io.setup(LASER_PIN, io.OUT)
io.output(LASER_PIN, LASER_State)

#-------------------------------
# initialise the camera
#-------------------------------
#camera = picamera.PiCamera()
#camera.hflip = True
#camera.vflip = True
#camera.resolution = (2592, 1944)

#-------------------------------
# initialise PWM device
#-------------------------------
pwm = PWM(0x40, debug=False)
pwm.setPWMFreq(50)   # pulse frequency in Hz

#-------------------------------
# servo power control
#-------------------------------
def toggleServoPower():
  global OE_State
  OE_State = not OE_State
  io.output(OE_PIN, OE_State)

def servosOff():
  global OE_State
  OE_State = io.HIGH
  io.output(OE_PIN, OE_State)

def servosOn():
  global OE_State
  OE_State = io.LOW
  io.output(OE_PIN, OE_State)

#-------------------------------
# laser power control
#-------------------------------
def toggleLaserPower():
  global LASER_State
  LASER_State = not LASER_State
  io.output(LASER_PIN, LASER_State)

def laserOff():
  global LASER_State
  LASER_State = io.LOW
  io.output(LASER_PIN, LASER_State)

def laserOn():
  global LASER_State
  LASER_State = io.HIGH
  io.output(LASER_PIN, LASER_State)

#-----------------------------------------------------------------------------
#   M A I N
#-----------------------------------------------------------------------------
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
curses.curs_set(0)
stdscr.keypad(1)
stdscr.resize(24,75)

while 1:
  #--------------------------
  # update display
  #--------------------------
  stdscr.clear()
  stdscr.border(0,0,0,0)

  stdscr.addstr(13,2,"q - quit")
  stdscr.addstr(14,2,"w,s,a,d - camera up,down,left,right ")
  stdscr.addstr(15,2,"i,k,j,l - laser up,down,left,right")
  stdscr.addstr(16,2,"[,] - servo step decrease,increase")
  stdscr.addstr(17,2,"p - toggle servo power")
  stdscr.addstr(18,2,"o - toggle laser power")
  stdscr.addstr(19,2,"e - toggle camera LED power")
  stdscr.addstr(20,2,"x - take a photo")
  stdscr.addstr(21,2,"` - store location in 1,2,3,4 (camera) 7,8,9,0 (laser)")

  stdscr.addstr(1,1,"CAMERA X = {0}  ".format(cameraXVal))
  stdscr.addstr(2,1,"       Y = {0}  ".format(cameraYVal))
  stdscr.addstr(1,25,"LASER X = {0}  ".format(laserXVal))
  stdscr.addstr(2,25,"      Y = {0}  ".format(laserYVal))

  stdscr.addstr(1,50,"SERVO STEP = {0}  ".format(servoStep))
  if OE_State == io.LOW:
    stdscr.addstr(2,50,"SERVOS ON ")
  else:
    stdscr.addstr(2,50,"SERVOS OFF")
  if LASER_State == io.HIGH:
    stdscr.addstr(4,25,"LASER ON ")
  else:
    stdscr.addstr(4,25,"LASER OFF")
  stdscr.addstr(4,1,"LED OFF")

  stdscr.refresh()
  #--------------------------
  # output to servos
  #--------------------------
  pwm.setPWM(laserXChan, 0, laserXVal)
  pwm.setPWM(laserYChan, 0, laserYVal)
  pwm.setPWM(cameraXChan, 0, cameraXVal)
  pwm.setPWM(cameraYChan, 0, cameraYVal)
  #--------------------------
  # get keypress and act on it
  #--------------------------
  c = stdscr.getch()
  if c == ord('q'):
    break
  if c == ord('p'):
    toggleServoPower()
  if c == ord('o'):
    toggleLaserPower()
  if c == ord('x'):
    stdscr.addstr(6,1,"taking photo...")
#    camera.capture('blah.jpg')
  if c == ord('['):
    servoStep -= 1
    servoStep = servoStep if servoStep > 1 else 1
  if c == ord(']'):
    servoStep += 1
    servoStep = servoStep if servoStep <= 50 else 50
  if OE_State == io.LOW:
    if c == ord('i'):
      laserYVal -= servoStep
      laserYVal = laserYVal if laserYVal > servoMin else servoMin
    if c == ord('k'):
      laserYVal += servoStep
      laserYVal = laserYVal if laserYVal < servoMax else servoMax
    if c == ord('j'):
      laserXVal -= servoStep
      laserXVal = laserXVal if laserXVal > servoMin else servoMin
    if c == ord('l'):
      laserXVal += servoStep
      laserXVal = laserXVal if laserXVal < servoMax else servoMax
    if c == ord('s'):
      cameraYVal -= servoStep
      cameraYVal = cameraYVal if cameraYVal > servoMin else servoMin
    if c == ord('w'):
      cameraYVal += servoStep
      cameraYVal = cameraYVal if cameraYVal < servoMax else servoMax
    if c == ord('a'):
      cameraXVal -= servoStep
      cameraXVal = cameraXVal if cameraXVal > servoMin else servoMin
    if c == ord('d'):
      cameraXVal += servoStep
      cameraXVal = cameraXVal if cameraXVal < servoMax else servoMax
    if c == ord('1'):
      cameraXVal = cameraS1[0]
      cameraYVal = cameraS1[1]
    if c == ord('2'):
      cameraXVal = cameraS2[0]
      cameraYVal = cameraS2[1]
    if c == ord('3'):
      cameraXVal = cameraS3[0]
      cameraYVal = cameraS3[1]
    if c == ord('4'):
      cameraXVal = cameraS4[0]
      cameraYVal = cameraS4[1]
    if c == ord('7'):
      laserXVal = laserS1[0]
      laserYVal = laserS1[1]
    if c == ord('8'):
      laserXVal = laserS2[0]
      laserYVal = laserS2[1]
    if c == ord('9'):
      laserXVal = laserS3[0]
      laserYVal = laserS3[1]
    if c == ord('0'):
      laserXVal = laserS4[0]
      laserYVal = laserS4[1]
    if c == ord('`'):
      c2 = stdscr.getch()
      if c2 == ord('1'):
        cameraS1 = (cameraXVal,cameraYVal)
      if c2 == ord('2'):
        cameraS2 = (cameraXVal,cameraYVal)
      if c2 == ord('3'):
        cameraS3 = (cameraXVal,cameraYVal)
      if c2 == ord('4'):
        cameraS4 = (cameraXVal,cameraYVal)
      if c2 == ord('7'):
        laserS1 = (laserXVal,laserYVal)
      if c2 == ord('8'):
        laserS2 = (laserXVal,laserYVal)
      if c2 == ord('9'):
        laserS3 = (laserXVal,laserYVal)
      if c2 == ord('0'):
        laserS4 = (laserXVal,laserYVal)

curses.nocbreak()
stdscr.keypad(0)
curses.echo()
curses.endwin()
#camera.close()
<commit_msg>removed laser_cursers.py<commit_after><|endoftext|>


<commit_before>from cv2 import __version__, findCirclesGrid,\
    CALIB_CB_ASYMMETRIC_GRID, calibrateCamera, undistort,\
    imwrite, CALIB_FIX_K4, CALIB_FIX_K5, CALIB_ZERO_TANGENT_DIST
from yaml import dump
import numpy as np
from threading import Thread
from time import strftime
from os.path import join, isdir
from os import makedirs
from random import choice
from string import ascii_uppercase
opencv_version = -1
if __version__.startswith('2'):
    opencv_version = 2
    from cv2 import SimpleBlobDetector
elif __version__.startswith('3'):
    opencv_version = 3
    from cv2 import SimpleBlobDetector_create
else:
    raise RuntimeError('OpenCV version not supported')

class State:

    """Keeps app state, providing query functions.

    See member functions for available states.

    """
    CORRECTING = 1
    CALIBRATING = 2 * CORRECTING
    ACQUIRING = 3 * CORRECTING
    KEYS = {ACQUIRING: 's', CALIBRATING: 's',
            CORRECTING: 'x'}

    def __init__(self, calibrator):
        """Initialise current state to recording.

        :param calibrator: `Calibrator` object for
        use in reporting, e.g. nr. of frames added
        """
        self.current = State.CORRECTING
        self.calibrator = calibrator

    def is_correcting(self):
        """

        :return: ``True`` if app currently online,
        i.e. correcting optical distortions
        """
        return self.current == State.CORRECTING

    def correcting(self):
        """Set current state to correcting.

        """
        self.current = State.CORRECTING

    def is_calibrating(self):
        """

        :return: ``True`` if app currently
        performing calibration
        """
        return self.current == State.CALIBRATING

    def calibrating(self):
        """Set current state to calibrating.

        """
        self.current = State.CALIBRATING

    def is_acquiring(self):
        """

        :return: ``True`` if app currently
        acquiring data for calibration
        """
        return self.current == State.ACQUIRING

    def acquiring(self):
        """Set current state to acquiring.

        """
        self.current = State.ACQUIRING

    def what(self):
        """

        :return: A string representing current
        state
        """
        if self.is_acquiring():
            msg = 'Acquired '
            if self.calibrator.grid_candidates:
                num_frames = len(self.calibrator.grid_candidates)
            else:
                num_frames = 0

            msg += str(num_frames)
            msg += ' frames '
            msg += '(min: '
            msg += str(Calibrator.MIN_FRAME_COUNT)
            msg += '), '
            if num_frames >= Calibrator.MIN_FRAME_COUNT:
                msg += State.KEYS[State.CALIBRATING]
                msg += ': calibrate, '
            msg += State.KEYS[State.CORRECTING]
            msg += ': abort acquisition'
        elif self.is_calibrating():
            msg = 'Performing calibration'
        elif self.is_correcting():
            msg = 'Correcting for optical distortions, '
            msg += State.KEYS[State.CALIBRATING]
            msg += ': start acquisition'
        msg += ', ESC: quit app'
        return msg

class Calibrator:

    """Keeps all data structures pertaining to calibration.

    """

    def __init__(self, pattern_specs, full, file_path=None, roi=None):
        """

        :param pattern_specs: Specs of used calibration
        pattern: ``width, height, row-spacing, col-spacing``
        :param full: Full frame specs: ``x, y, width, height``
        :param file_path: YAML file with calibration params
        :param roi: Sub frame specs: ``x, y, width, height``
        """
        self.pattern_dims = tuple(int(val) for val in pattern_specs[:2])
        self.pattern_spacing = pattern_specs[2:]
        if opencv_version == 2:
            self.detector = SimpleBlobDetector()
        elif opencv_version == 3:
            self.detector = SimpleBlobDetector_create()
        d_y = self.pattern_spacing[0]  # 3.0
        d_x = self.pattern_spacing[1]  # 1.0
        width = int(pattern_specs[0])
        height = int(pattern_specs[1])
        self.grid = np.zeros((width * height, 3), np.float32)
        self.grid[:, :2] = np.mgrid[0:width, 0:height].T.reshape(-1, 2)
        self.grid[:, 0] *= d_y
        self.grid[:, 1] *= d_x
        for start in range(width, width*height, 2*width):
            self.grid[start:start + width, 0] += d_y / 2
        self.grids = None
        self.grid_candidates = None
        self.image_size = None
        self.camera_matrix = None
        self.dist_coeffs = None
        Calibrator.MIN_FRAME_COUNT = 10
        self.calibration_thread = None
        self.roi = full
        if roi is not None:
            self.roi = roi
        self.full = full
        self.reset()
        # TODO file_path
        pass

    def reset(self):
        """Reset all data saved so far.

        """
        self.grids = []
        self.grid_candidates = []
        self.image_size = []
        self.camera_matrix = None  # TODO identity
        self.dist_coeffs = None  # TODO identity (??)
        # TODO
        pass

    def can(self):
        """

        :return: ``True`` if calibration can
        be performed.
        """
        return len(self.grid_candidates) >= Calibrator.MIN_FRAME_COUNT

    def correct(self, image):
        """Correct passed image.

        :param image:
        :return: ``retval`` flag and corrected image
        """
        ret = False
        corrected_image = None
        if self.camera_matrix is not None and \
           self.dist_coeffs is not None:
            corrected_image = undistort(image,
                                        self.camera_matrix,
                                        self.dist_coeffs)
            ret = True
        # TODO - if using identity?
        return ret, corrected_image

    def __calibrate(self, file_path):
        """Perform calibration, to be called as a separate
        thread.

        :param file_path: Where to save resulting calibration
        """
        ret, camera_matrix, dist_coeffs, rvecs, tvecs = \
            calibrateCamera(objectPoints=self.grids,
                            imagePoints=self.grid_candidates,
                            imageSize=self.image_size,
                            flags=CALIB_FIX_K4 |
                            CALIB_FIX_K5 |
                            CALIB_ZERO_TANGENT_DIST)
        if ret:
            self.camera_matrix = camera_matrix
            self.dist_coeffs = dist_coeffs
            if file_path is not None:
                calibration_file = open(file_path, 'w')
                calibration = dict(camera_matrix=str(self.camera_matrix),
                                   dist_coeffs=str(self.dist_coeffs),
                                   roi=str(self.roi),
                                   full=str(self.full))
                calibration_file.write(dump(calibration,
                                            default_flow_style=False))
                calibration_file.close()

    def start(self, file_path):
        """Start calibration thread.

        :param file_path: Where to save calibration upon
        success
        :return: ``True`` if thread started successfully.
        """
        if self.calibration_thread is None:
            self.calibration_thread = Thread(target=self.__calibrate,
                                             kwargs={'file_path': file_path})
            try:
                self.calibration_thread.start()
            except RuntimeError:
                self.calibration_thread = None
                return False
            else:
                return True
        return True

    def done(self):
        """

        :return: Whether calibration thread has finished.
        """
        if self.calibration_thread is None:
            return True

        if not self.calibration_thread.is_alive():
            self.calibration_thread = None
            return True

        return False

    def append(self, image, file_path=None):
        """Append image as one of frames to be used in
        calibration.

        :param image:
        :param file_path: If passed, `image` is saved
        here
        :return: ``True`` if `image` usable, ``False``
        otherwise, in addition to detected circle grid
        of blobs
        """
        blobs = None
        ret, blobs = findCirclesGrid(image, self.pattern_dims,
                                     blobs, CALIB_CB_ASYMMETRIC_GRID)
        if ret:
            self.image_size = image.shape[:2]  # TODO what about the -1 I saw in sample code?
            self.grid_candidates.append(blobs)
            self.grids.append(self.grid)

        if file_path is not None:
            imwrite(file_path, image)

        return ret, blobs

class FileIO:

    """Manages file IO parameters, like session folders.

    """

    def __init__(self, output_folder):
        """

        :param output_folder: Creates session folders
        inside this one
        """
        self.output_folder = output_folder
        self.session_folder = None
        self.current_image = 1

    def new_session(self):
        """Create a new session folder to save current
        calibration data.

        """
        unique_suffix = ''.join(choice(ascii_uppercase) for _ in range(5))
        session_folder = join(self.output_folder,
                              strftime('%Y-%m-%d-%H-%M-%S') +
                              '-' + unique_suffix)
        makedirs(session_folder)
        if isdir(session_folder):
            self.session_folder = session_folder
        self.current_image = 1

    def calibration(self):
        """

        :return: File path where current calibration is
        to be saved
        """
        if self.session_folder is None:
            return None

        return join(self.session_folder, 'calibration.yml')

    def next_image(self):
        """Increment image count and get file path for
        saving next image.

        :return: File path where an image appended to
        calibration data is to be saved
        """
        if self.session_folder is None:
            return None

        file_path = join(self.session_folder,
                         'frame_' +
                         str(self.current_image).zfill(3) +
                         '.jpg')
        self.current_image += 1
        return file_path
<commit_msg>Issue #14, #19: specifying cameraMatrix and distCoeffs explicitly for calibrateCamera, as required by OpenCV3<commit_after>from cv2 import __version__, findCirclesGrid,\
    CALIB_CB_ASYMMETRIC_GRID, calibrateCamera, undistort,\
    imwrite, CALIB_FIX_K4, CALIB_FIX_K5, CALIB_ZERO_TANGENT_DIST
from yaml import dump
import numpy as np
from threading import Thread
from time import strftime
from os.path import join, isdir
from os import makedirs
from random import choice
from string import ascii_uppercase
opencv_version = -1
if __version__.startswith('2'):
    opencv_version = 2
    from cv2 import SimpleBlobDetector
elif __version__.startswith('3'):
    opencv_version = 3
    from cv2 import SimpleBlobDetector_create
else:
    raise RuntimeError('OpenCV version not supported')

class State:

    """Keeps app state, providing query functions.

    See member functions for available states.

    """
    CORRECTING = 1
    CALIBRATING = 2 * CORRECTING
    ACQUIRING = 3 * CORRECTING
    KEYS = {ACQUIRING: 's', CALIBRATING: 's',
            CORRECTING: 'x'}

    def __init__(self, calibrator):
        """Initialise current state to recording.

        :param calibrator: `Calibrator` object for
        use in reporting, e.g. nr. of frames added
        """
        self.current = State.CORRECTING
        self.calibrator = calibrator

    def is_correcting(self):
        """

        :return: ``True`` if app currently online,
        i.e. correcting optical distortions
        """
        return self.current == State.CORRECTING

    def correcting(self):
        """Set current state to correcting.

        """
        self.current = State.CORRECTING

    def is_calibrating(self):
        """

        :return: ``True`` if app currently
        performing calibration
        """
        return self.current == State.CALIBRATING

    def calibrating(self):
        """Set current state to calibrating.

        """
        self.current = State.CALIBRATING

    def is_acquiring(self):
        """

        :return: ``True`` if app currently
        acquiring data for calibration
        """
        return self.current == State.ACQUIRING

    def acquiring(self):
        """Set current state to acquiring.

        """
        self.current = State.ACQUIRING

    def what(self):
        """

        :return: A string representing current
        state
        """
        if self.is_acquiring():
            msg = 'Acquired '
            if self.calibrator.grid_candidates:
                num_frames = len(self.calibrator.grid_candidates)
            else:
                num_frames = 0

            msg += str(num_frames)
            msg += ' frames '
            msg += '(min: '
            msg += str(Calibrator.MIN_FRAME_COUNT)
            msg += '), '
            if num_frames >= Calibrator.MIN_FRAME_COUNT:
                msg += State.KEYS[State.CALIBRATING]
                msg += ': calibrate, '
            msg += State.KEYS[State.CORRECTING]
            msg += ': abort acquisition'
        elif self.is_calibrating():
            msg = 'Performing calibration'
        elif self.is_correcting():
            msg = 'Correcting for optical distortions, '
            msg += State.KEYS[State.CALIBRATING]
            msg += ': start acquisition'
        msg += ', ESC: quit app'
        return msg

class Calibrator:

    """Keeps all data structures pertaining to calibration.

    """

    def __init__(self, pattern_specs, full, file_path=None, roi=None):
        """

        :param pattern_specs: Specs of used calibration
        pattern: ``width, height, row-spacing, col-spacing``
        :param full: Full frame specs: ``x, y, width, height``
        :param file_path: YAML file with calibration params
        :param roi: Sub frame specs: ``x, y, width, height``
        """
        self.pattern_dims = tuple(int(val) for val in pattern_specs[:2])
        self.pattern_spacing = pattern_specs[2:]
        if opencv_version == 2:
            self.detector = SimpleBlobDetector()
        elif opencv_version == 3:
            self.detector = SimpleBlobDetector_create()
        d_y = self.pattern_spacing[0]  # 3.0
        d_x = self.pattern_spacing[1]  # 1.0
        width = int(pattern_specs[0])
        height = int(pattern_specs[1])
        self.grid = np.zeros((width * height, 3), np.float32)
        self.grid[:, :2] = np.mgrid[0:width, 0:height].T.reshape(-1, 2)
        self.grid[:, 0] *= d_y
        self.grid[:, 1] *= d_x
        for start in range(width, width*height, 2*width):
            self.grid[start:start + width, 0] += d_y / 2
        self.grids = None
        self.grid_candidates = None
        self.image_size = None
        self.camera_matrix = None
        self.dist_coeffs = None
        Calibrator.MIN_FRAME_COUNT = 10
        self.calibration_thread = None
        self.roi = full
        if roi is not None:
            self.roi = roi
        self.full = full
        self.reset()
        # TODO file_path
        pass

    def reset(self):
        """Reset all data saved so far.

        """
        self.grids = []
        self.grid_candidates = []
        self.image_size = []
        self.camera_matrix = None  # TODO identity
        self.dist_coeffs = None  # TODO identity (??)
        # TODO
        pass

    def can(self):
        """

        :return: ``True`` if calibration can
        be performed.
        """
        return len(self.grid_candidates) >= Calibrator.MIN_FRAME_COUNT

    def correct(self, image):
        """Correct passed image.

        :param image:
        :return: ``retval`` flag and corrected image
        """
        ret = False
        corrected_image = None
        if self.camera_matrix is not None and \
           self.dist_coeffs is not None:
            corrected_image = undistort(image,
                                        self.camera_matrix,
                                        self.dist_coeffs)
            ret = True
        # TODO - if using identity?
        return ret, corrected_image

    def __calibrate(self, file_path):
        """Perform calibration, to be called as a separate
        thread.

        :param file_path: Where to save resulting calibration
        """
        ret, camera_matrix, dist_coeffs, rvecs, tvecs = \
            calibrateCamera(objectPoints=self.grids,
                            imagePoints=self.grid_candidates,
                            imageSize=self.image_size,
                            cameraMatrix=None,
                            distCoeffs=None,
                            flags=CALIB_FIX_K4 |
                            CALIB_FIX_K5 |
                            CALIB_ZERO_TANGENT_DIST)
        if ret:
            self.camera_matrix = camera_matrix
            self.dist_coeffs = dist_coeffs
            if file_path is not None:
                calibration_file = open(file_path, 'w')
                calibration = dict(camera_matrix=str(self.camera_matrix),
                                   dist_coeffs=str(self.dist_coeffs),
                                   roi=str(self.roi),
                                   full=str(self.full))
                calibration_file.write(dump(calibration,
                                            default_flow_style=False))
                calibration_file.close()

    def start(self, file_path):
        """Start calibration thread.

        :param file_path: Where to save calibration upon
        success
        :return: ``True`` if thread started successfully.
        """
        if self.calibration_thread is None:
            self.calibration_thread = Thread(target=self.__calibrate,
                                             kwargs={'file_path': file_path})
            try:
                self.calibration_thread.start()
            except RuntimeError:
                self.calibration_thread = None
                return False
            else:
                return True
        return True

    def done(self):
        """

        :return: Whether calibration thread has finished.
        """
        if self.calibration_thread is None:
            return True

        if not self.calibration_thread.is_alive():
            self.calibration_thread = None
            return True

        return False

    def append(self, image, file_path=None):
        """Append image as one of frames to be used in
        calibration.

        :param image:
        :param file_path: If passed, `image` is saved
        here
        :return: ``True`` if `image` usable, ``False``
        otherwise, in addition to detected circle grid
        of blobs
        """
        blobs = None
        ret, blobs = findCirclesGrid(image, self.pattern_dims,
                                     blobs, CALIB_CB_ASYMMETRIC_GRID)
        if ret:
            self.image_size = image.shape[:2]  # TODO what about the -1 I saw in sample code?
            self.grid_candidates.append(blobs)
            self.grids.append(self.grid)

        if file_path is not None:
            imwrite(file_path, image)

        return ret, blobs

class FileIO:

    """Manages file IO parameters, like session folders.

    """

    def __init__(self, output_folder):
        """

        :param output_folder: Creates session folders
        inside this one
        """
        self.output_folder = output_folder
        self.session_folder = None
        self.current_image = 1

    def new_session(self):
        """Create a new session folder to save current
        calibration data.

        """
        unique_suffix = ''.join(choice(ascii_uppercase) for _ in range(5))
        session_folder = join(self.output_folder,
                              strftime('%Y-%m-%d-%H-%M-%S') +
                              '-' + unique_suffix)
        makedirs(session_folder)
        if isdir(session_folder):
            self.session_folder = session_folder
        self.current_image = 1

    def calibration(self):
        """

        :return: File path where current calibration is
        to be saved
        """
        if self.session_folder is None:
            return None

        return join(self.session_folder, 'calibration.yml')

    def next_image(self):
        """Increment image count and get file path for
        saving next image.

        :return: File path where an image appended to
        calibration data is to be saved
        """
        if self.session_folder is None:
            return None

        file_path = join(self.session_folder,
                         'frame_' +
                         str(self.current_image).zfill(3) +
                         '.jpg')
        self.current_image += 1
        return file_path
<|endoftext|>


<commit_before>import functools
import hashlib
import hmac
import subprocess

from flask import abort
from flask import request
from flask import session
from flask_login import current_user
from flask_socketio import disconnect
from peewee import DoesNotExist

from home import settings
from home.core.async import run
from home.core.models import get_device
from home.core.utils import random_string, method_from_name
from home.web.models import APIClient, Subscriber

try:
    VERSION = 'v' + subprocess.check_output(['git', 'describe', '--tags', 'HEAD']).decode('UTF-8')
except:
    VERSION = 'unknown'

logger = None

def ws_login_required(f):
    """
    Authenticate Websocket requests
    :param f: Decorated function
    :return: Function
    """

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        if not current_user.is_authenticated:
            disconnect()
        else:
            return f(*args, **kwargs)

    return wrapped

def ws_optional_auth(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        if current_user.is_authenticated:
            kwargs['auth'] = True
        return f(*args, **kwargs)

    return wrapped

def api_auth_required(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        try:
            if request.headers.get('X-Gogs-Signature'):
                client = APIClient.get(name='gogs-update')
                secret = bytes(client.token.encode())
                mac = hmac.new(secret, msg=request.get_data(), digestmod=hashlib.sha256)
                if not hmac.compare_digest(mac.hexdigest(), request.headers['X-Gogs-Signature']):
                    abort(403)
            else:
                client = APIClient.get(token=request.values.get('key'))
            kwargs['client'] = client
        except DoesNotExist:
            if settings.DEBUG:
                kwargs['client'] = APIClient.get()
            else:
                abort(403)
        return f(*args, **kwargs)

    return wrapped

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = random_string()
    return session['_csrf_token']

def send_to_subscribers(message):
    for subscriber in Subscriber.select():
        try:
            subscriber.push(message)
        except Exception as e:
            print("Webpusher:", str(e))

def handle_task(post, client):
    device = get_device(post.pop('device'))
    c_method, c_kwargs = device.last.pop()
    l_method, l_kwargs = device.last.pop()
    device.last.append((l_method, l_kwargs))
    device.last.append((c_method, c_kwargs))
    if post.get('method') == 'last':
        # The most recent action
        # The previous action
        method = l_method
        kwargs = l_kwargs
    else:
        print(device, device.dev, post.get('method'))
        method = method_from_name(device.dev, post.pop('method'))
        if post.get('increment'):
            kwargs = c_kwargs
            kwargs[post['increment']] += post.get('count', 1)
        elif post.get('decrement'):
            kwargs = c_kwargs
            kwargs[post['decrement']] += post.get('count', 1)
        else:
            kwargs = post
    device.last.append((method, kwargs))
    from home.web.web import app
    app.logger.info(
        "({}) Execute {} on {} with config {}".format(client.name, method.__name__, device.name, kwargs))
    if device.driver.noserialize:
        method(**kwargs)
    else:
        device.last_task = run(method, **kwargs)
<commit_msg>Should fix bug<commit_after>import functools
import hashlib
import hmac
import subprocess

from flask import abort
from flask import request
from flask import session
from flask_login import current_user
from flask_socketio import disconnect
from peewee import DoesNotExist

from home import settings
from home.core.async import run
from home.core.models import get_device
from home.core.utils import random_string, method_from_name
from home.web.models import APIClient, Subscriber

try:
    VERSION = 'v' + subprocess.check_output(['git', 'describe', '--tags', 'HEAD']).decode('UTF-8')
except:
    VERSION = 'unknown'

logger = None

def ws_login_required(f):
    """
    Authenticate Websocket requests
    :param f: Decorated function
    :return: Function
    """

    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        if not current_user.is_authenticated:
            disconnect()
        else:
            return f(*args, **kwargs)

    return wrapped

def ws_optional_auth(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        if current_user.is_authenticated:
            kwargs['auth'] = True
        return f(*args, **kwargs)

    return wrapped

def api_auth_required(f):
    @functools.wraps(f)
    def wrapped(*args, **kwargs):
        try:
            if request.headers.get('X-Gogs-Signature'):
                client = APIClient.get(name='gogs-update')
                secret = bytes(client.token.encode())
                mac = hmac.new(secret, msg=request.get_data(), digestmod=hashlib.sha256)
                if not hmac.compare_digest(mac.hexdigest(), request.headers['X-Gogs-Signature']):
                    abort(403)
            else:
                client = APIClient.get(token=request.values.get('key'))
            kwargs['client'] = client
        except DoesNotExist:
            if settings.DEBUG:
                kwargs['client'] = APIClient.get()
            else:
                abort(403)
        return f(*args, **kwargs)

    return wrapped

def generate_csrf_token():
    if '_csrf_token' not in session:
        session['_csrf_token'] = random_string()
    return session['_csrf_token']

def send_to_subscribers(message):
    for subscriber in Subscriber.select():
        try:
            subscriber.push(message)
        except Exception as e:
            print("Webpusher:", str(e))

def handle_task(post, client):
    device = get_device(post.pop('device'))
    try:
        c_method, c_kwargs = device.last.pop()
    except IndexError:
        c_method, c_kwargs = None, None
    try:
        l_method, l_kwargs = device.last.pop()
    except IndexError:
        l_method, l_kwargs = None, None
    else:
        device.last.append((l_method, l_kwargs))
        device.last.append((c_method, c_kwargs))
    if post.get('method') == 'last':
        # The most recent action
        # The previous action
        method = l_method
        kwargs = l_kwargs
    else:
        print(device, device.dev, post.get('method'))
        method = method_from_name(device.dev, post.pop('method'))
        if post.get('increment'):
            kwargs = c_kwargs
            kwargs[post['increment']] += post.get('count', 1)
        elif post.get('decrement'):
            kwargs = c_kwargs
            kwargs[post['decrement']] += post.get('count', 1)
        else:
            kwargs = post
    device.last.append((method, kwargs))
    from home.web.web import app
    app.logger.info(
        "({}) Execute {} on {} with config {}".format(client.name, method.__name__, device.name, kwargs))
    if device.driver.noserialize:
        method(**kwargs)
    else:
        device.last_task = run(method, **kwargs)
<|endoftext|>


<commit_before>class MatplotlibBackendSetter(object):
    """
    Import hook to make sure the proper Qt backend is set when importing
    Matplotlib.
    """

    enabled = True

    def find_module(self, mod_name, pth):
        if self.enabled and 'matplotlib' in mod_name:
            self.enabled = False
            set_mpl_backend()
        return

def set_mpl_backend():

    from matplotlib import rcParams, rcdefaults

    # standardize mpl setup
    rcdefaults()

    from glue.external.qt import is_pyqt5
    if is_pyqt5():
        rcParams['backend'] = 'Qt5Agg'
    else:
        rcParams['backend'] = 'Qt4Agg'

    # The following is a workaround for the fact that Matplotlib checks the
    # rcParams at import time, not at run-time. I have opened an issue with
    # Matplotlib here: https://github.com/matplotlib/matplotlib/issues/5513
    from matplotlib import get_backend
    from matplotlib import backends
    backends.backend = get_backend()
<commit_msg>Add missing find_spec for import hook, to avoid issues when trying to set colormap<commit_after>class MatplotlibBackendSetter(object):
    """
    Import hook to make sure the proper Qt backend is set when importing
    Matplotlib.
    """

    enabled = True

    def find_module(self, mod_name, pth):
        if self.enabled and 'matplotlib' in mod_name:
            self.enabled = False
            set_mpl_backend()

    def find_spec(self, name, import_path, target_module=None):
        pass

def set_mpl_backend():

    from matplotlib import rcParams, rcdefaults

    # standardize mpl setup
    rcdefaults()

    from glue.external.qt import is_pyqt5
    if is_pyqt5():
        rcParams['backend'] = 'Qt5Agg'
    else:
        rcParams['backend'] = 'Qt4Agg'

    # The following is a workaround for the fact that Matplotlib checks the
    # rcParams at import time, not at run-time. I have opened an issue with
    # Matplotlib here: https://github.com/matplotlib/matplotlib/issues/5513
    from matplotlib import get_backend
    from matplotlib import backends
    backends.backend = get_backend()
<|endoftext|>


<commit_before>"""
===============================================
Cross-validation on diabetes Dataset Exercise
===============================================

A tutorial exercise which uses cross-validation with linear models.

This exercise is used in the :ref:`cv_estimators_tut` part of the
:ref:`model_selection_tut` section of the :ref:`stat_learn_tut_index`.
"""

from __future__ import print_function
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.linear_model import LassoCV
from sklearn.linear_model import Lasso
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

diabetes = datasets.load_diabetes()
X = diabetes.data[:150]
y = diabetes.target[:150]

lasso = Lasso(random_state=0)
alphas = np.logspace(-4, -0.5, 30)

scores = list()
scores_std = list()

n_folds = 3

for alpha in alphas:
    lasso.alpha = alpha
    this_scores = cross_val_score(lasso, X, y, cv=n_folds, n_jobs=1)
    scores.append(np.mean(this_scores))
    scores_std.append(np.std(this_scores))

scores, scores_std = np.array(scores), np.array(scores_std)

plt.figure().set_size_inches(8, 6)
plt.semilogx(alphas, scores)

# plot error lines showing +/- std. errors of the scores
std_error = scores_std / np.sqrt(n_folds)

plt.semilogx(alphas, scores + std_error, 'b--')
plt.semilogx(alphas, scores - std_error, 'b--')

# alpha=0.2 controls the translucency of the fill color
plt.fill_between(alphas, scores + std_error, scores - std_error, alpha=0.2)

plt.ylabel('CV score')
plt.xlabel('alpha')
plt.axhline(np.max(scores), linestyle='--', color='.5')

##############################################################################
# Bonus: how much can you trust the selection of alpha?

# To answer this question we use the LassoCV object that sets its alpha
# parameter automatically from the data by internal cross-validation (i.e. it
# performs cross-validation on the training data it receives).
# We use external cross-validation to see how much the automatically obtained
# alphas differ across different cross-validation folds.
lasso_cv = LassoCV(alphas=alphas, random_state=0)
k_fold = KFold(3)

print("Answer to the bonus question:",
      "how much can you trust the selection of alpha?")
print()
print("Alpha parameters maximising the generalization score on different")
print("subsets of the data:")
for k, (train, test) in enumerate(k_fold.split(X, y)):
    lasso_cv.fit(X[train], y[train])
    print("[fold {0}] alpha: {1:.5f}, score: {2:.5f}".
          format(k, lasso_cv.alpha_, lasso_cv.score(X[test], y[test])))
print()
print("Answer: Not very much since we obtained different alphas for different")
print("subsets of the data and moreover, the scores for these alphas differ")
print("quite substantially.")

plt.show()
<commit_msg>polish figure<commit_after>"""
===============================================
Cross-validation on diabetes Dataset Exercise
===============================================

A tutorial exercise which uses cross-validation with linear models.

This exercise is used in the :ref:`cv_estimators_tut` part of the
:ref:`model_selection_tut` section of the :ref:`stat_learn_tut_index`.
"""

from __future__ import print_function
print(__doc__)

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
from sklearn.linear_model import LassoCV
from sklearn.linear_model import Lasso
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

diabetes = datasets.load_diabetes()
X = diabetes.data[:150]
y = diabetes.target[:150]

lasso = Lasso(random_state=0)
alphas = np.logspace(-4, -0.5, 30)

scores = list()
scores_std = list()

n_folds = 3

for alpha in alphas:
    lasso.alpha = alpha
    this_scores = cross_val_score(lasso, X, y, cv=n_folds, n_jobs=1)
    scores.append(np.mean(this_scores))
    scores_std.append(np.std(this_scores))

scores, scores_std = np.array(scores), np.array(scores_std)

plt.figure().set_size_inches(8, 6)
plt.semilogx(alphas, scores)

# plot error lines showing +/- std. errors of the scores
std_error = scores_std / np.sqrt(n_folds)

plt.semilogx(alphas, scores + std_error, 'b--')
plt.semilogx(alphas, scores - std_error, 'b--')

# alpha=0.2 controls the translucency of the fill color
plt.fill_between(alphas, scores + std_error, scores - std_error, alpha=0.2)

plt.ylabel('CV score +/- std error')
plt.xlabel('alpha')
plt.axhline(np.max(scores), linestyle='--', color='.5')
plt.xlim([alphas[0], alphas[-1]])

##############################################################################
# Bonus: how much can you trust the selection of alpha?

# To answer this question we use the LassoCV object that sets its alpha
# parameter automatically from the data by internal cross-validation (i.e. it
# performs cross-validation on the training data it receives).
# We use external cross-validation to see how much the automatically obtained
# alphas differ across different cross-validation folds.
lasso_cv = LassoCV(alphas=alphas, random_state=0)
k_fold = KFold(3)

print("Answer to the bonus question:",
      "how much can you trust the selection of alpha?")
print()
print("Alpha parameters maximising the generalization score on different")
print("subsets of the data:")
for k, (train, test) in enumerate(k_fold.split(X, y)):
    lasso_cv.fit(X[train], y[train])
    print("[fold {0}] alpha: {1:.5f}, score: {2:.5f}".
          format(k, lasso_cv.alpha_, lasso_cv.score(X[test], y[test])))
print()
print("Answer: Not very much since we obtained different alphas for different")
print("subsets of the data and moreover, the scores for these alphas differ")
print("quite substantially.")

plt.show()
<|endoftext|>


<commit_before>#!/usr/bin/python

#
# Copyright (c) 2015 Pablo Cases,
# Distributed under the MIT License.
# (See accompanying file LICENSE file or
# copy at http://opensource.org/licenses/MIT)
#

import sys
import os
import signal
import subprocess
import logging
import logging.handlers
import inspect
import gobject
import dbus
from dbus.mainloop.glib import DBusGMainLoop

class GEndSessionListenerBase(object):

    # override this method when subclassing
    # this method contains the actions performed during logout/"Gnome EndSession"
    def end_session_actions(self):
        # According to Gnome Session Manager docs:
        # "The client must not attempt to interact with the user [...].
        # The application will be given a maxium of ten seconds to perform
        # any actions required for a clean shutdown."
        raise NotImplementedError()

    # start() is a blocking method and returns only
    # during logout or if interrupted by a Unix signal
    def start(self):
	# signals can only be received while the event loop is running
        self.__loop = gobject.MainLoop()
        self.__loop.run()

        # run actions when interrupted by a Unix signal
        if self.interrupted:
            self.end_session_actions()

        self.logger.info("Terminated")

    def __init__(self):
        # will be set to True if a Unix signal is caught and handled
        self.interrupted = False

        # handle Unix signals
        #  (SIGKILL and SIGSTOP cannot be caught, blocked, or ignored)
        signal.signal(signal.SIGHUP, self.__signal_handler)
        signal.signal(signal.SIGINT, self.__signal_handler) # ctrl-c
        signal.signal(signal.SIGQUIT, self.__signal_handler) # ctrl-\
        signal.signal(signal.SIGTERM, self.__signal_handler)
        signal.signal(signal.SIGUSR1, signal.SIG_IGN)
        signal.signal(signal.SIGUSR2, signal.SIG_IGN)
        signal.signal(signal.SIGTSTP, self.__signal_handler) # ctrl-z
        # the remaining signals are considered fatal and are left unhandled

        # create the logger object
        FORMAT = os.path.basename(inspect.getfile(self.__class__)) + ": %(message)s"
        self.logger = logging.getLogger()
        formatter = logging.Formatter(FORMAT)
        handler = logging.handlers.SysLogHandler(address = "/dev/log")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)

        # must be setup to receive signals
        DBusGMainLoop(set_as_default=True)

        # connect to the appropriate bus
        session_bus = dbus.SessionBus()

        # create the proxy session manager objects for calling methods
        session_manager = session_bus.get_object("org.gnome.SessionManager",
        					 "/org/gnome/SessionManager")
        self.__session_manager_iface = dbus.Interface(session_manager,
            dbus_interface="org.gnome.SessionManager")

        # example: diplays the logout dialog
        #session_manager_iface.Logout(0)

        # registers a client with the session manager
        self.__client_id = self.__session_manager_iface.RegisterClient("", "")
        #self.logger.debug("Gnome Session Manager Client ID = %s" % self.__client_id)

        # create the proxy session manager client objects for calling methods
        session_client = session_bus.get_object("org.gnome.SessionManager",
                                                self.__client_id)
        self.__session_client_private_iface = dbus.Interface(session_client,
                    dbus_interface="org.gnome.SessionManager.ClientPrivate")

        # Introspectable interfaces define a property 'Introspect' that
        # will return an XML string that describes the object's interface
        #introspection_iface = dbus.Interface(
        #    session_client,
        #    dbus.INTROSPECTABLE_IFACE,
        #)
        #self.logger.debug(introspection_iface.Introspect())
        # NOTE: The org.gnome.SessionManager.ClientPrivate is not displayed for Introspect() calls

        session_bus.add_signal_receiver(self.__query_end_session_handler,
        	signal_name = "QueryEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__end_session_handler,
        	signal_name = "EndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__cancel_end_session_handler,
        	signal_name = "CancelEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__stop_handler,
        	signal_name = "Stop",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        self.logger.info("Initialized")

    def __teardown(self):
        self.__loop.quit()
        self.__session_manager_iface.UnregisterClient(self.__client_id)
        self.logger.info("Terminated DBus connections")

    #
    # Handle Unix signals
    #
    def __signal_handler(self, signum, stack):
        self.logger.info("Received shell signal: %s" % signum)
	# cannot call end_session_actions here as this may include
        # subprocess.call() or equivalent that is not permitted during signal handling
        # delay execution of the actions until executing the main thread
        self.interrupted = True
        self.__teardown()

    #
    # Methods defined in org.gnome.SessionManager.ClientPrivate
    #
    def __end_session_response(self, ok=True):
        if ok:
            self.logger.info("Calling DBus method:  EndSessionResponse(True, \"\")")
            self.__session_client_private_iface.EndSessionResponse(True, "")
        else:
            # NOTE: calling EndSessionResponse(False) does not inhibit logout on Ubuntu 14.10...
            self.logger.info("Calling DBus method:  EndSessionResponse(False, \"Not ready\")")
            self.__session_client_private_iface.EndSessionResponse(False, "Not ready")

    #
    # Signals defined in org.gnome.SessionManager.ClientPrivate
    # flags defined in GsmClientEndSessionFlag
    #       https://git.gnome.org/browse/gnome-session/tree/gnome-session/gsm-client.h
    # flags == 0: normal logout
    # flags == 1: Forced logout and content of EndSessionResponse will be ignored
    #
    def __query_end_session_handler(self, flags):
        self.logger.info("Received DBus signal: QueryEndSession(%d)" % flags)
        # ignore flags, always agree on the QueryEndSesion
        self.__end_session_response(True)

    def __end_session_handler(self, flags):
        self.logger.info("Received DBus signal: EndSession(%d)" % flags)
        self.end_session_actions()
        self.__end_session_response(True)

    def __cancel_end_session_handler(self):
        self.logger.info("Received DBus signal: CancelEndSession")
        #NOTE: ignored for now, i.e. known limitation

    def __stop_handler(self):
        self.logger.info("Received DBus signal: Stop")
        self.__teardown()

#
# Gnome End Session Listener
#
class GEndSessionListener(GEndSessionListenerBase):

    def __init__(self, cmdline):
        self.cmdline = cmdline
        super(GEndSessionListener, self).__init__()

    def end_session_actions(self):
        self.logger.debug("Performing user specified logout actions (%s)" % self.cmdline)

        cmd = self.cmdline[0]
        if not os.path.isabs(cmd):
            self.cmdline.pop(0)
            self.cmdline.insert(0, "./%s" % cmd)

        # NOTE: returncode is not checked as EndSessionResponse(False) does not
        # appear to abort the logout process.
        subprocess.call(self.cmdline)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "Usage: %s <end-session-script> [end-session-script-args]" % sys.argv[0]
        print
        print "          end-session-script refers to the path to the executable script "
        print "          to be run when the Gnome Session ends."
        print
        exit(1)

    cmdline = sys.argv[1:]
    #if not (os.path.isfile(end_session_script) and os.access(end_session_script, os.X_OK)):
    if not (os.access(cmdline[0], os.F_OK) and os.access(cmdline[0], os.X_OK)):
        print "The file '%s'  does not exist or is not an executable. Abort." % cmdline[0]
        exit(1)

    listener = GEndSessionListener(cmdline)
    listener.start()
<commit_msg> Update to Python3 and PyGObject Introspection<commit_after>#!/usr/bin/python3

#
# Copyright (c) 2015-2020 Pablo Cases,
# Distributed under the MIT License.
# (See accompanying file LICENSE file or
# copy at http://opensource.org/licenses/MIT)
#

import sys
import os
from dbus.mainloop.glib import DBusGMainLoop
import dbus
from gi.repository import GLib
import datetime
import signal
import subprocess
import logging
import logging.handlers
import inspect

class GEndSessionListenerBase(object):

    # override this method when subclassing
    # this method contains the actions performed during logout/"Gnome EndSession"
    def end_session_actions(self):
        # According to Gnome Session Manager docs:
        # "The client must not attempt to interact with the user [...].
        # The application will be given a maxium of ten seconds to perform
        # any actions required for a clean shutdown."
        raise NotImplementedError()

    # start() is a blocking method and returns only
    # during logout or if interrupted by a Unix signal
    def start(self):
	# signals can only be received while the event loop is running
        self.__loop = GLib.MainLoop()
        self.__loop.run()

        # run actions when interrupted by a Unix signal
        if self.interrupted:
            self.end_session_actions()

        self.logger.info("Terminated")

    def __init__(self):
        # will be set to True if a Unix signal is caught and handled
        self.interrupted = False

        # handle Unix signals
        #  (SIGKILL and SIGSTOP cannot be caught, blocked, or ignored)
        signal.signal(signal.SIGHUP, self.__signal_handler)
        signal.signal(signal.SIGINT, self.__signal_handler) # ctrl-c
        signal.signal(signal.SIGQUIT, self.__signal_handler) # ctrl-\
        signal.signal(signal.SIGTERM, self.__signal_handler)
        signal.signal(signal.SIGUSR1, signal.SIG_IGN)
        signal.signal(signal.SIGUSR2, signal.SIG_IGN)
        signal.signal(signal.SIGTSTP, self.__signal_handler) # ctrl-z
        # the remaining signals are considered fatal and are left unhandled

        # create the logger object
        FORMAT = os.path.basename(inspect.getfile(self.__class__)) + ": %(message)s"
        self.logger = logging.getLogger()
        formatter = logging.Formatter(FORMAT)
        handler = logging.handlers.SysLogHandler(address = "/dev/log")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)

        # must be setup to receive signals
        DBusGMainLoop(set_as_default=True)

        # connect to the appropriate bus
        session_bus = dbus.SessionBus()

        # create the proxy session manager objects for calling methods
        session_manager = session_bus.get_object("org.gnome.SessionManager",
        					 "/org/gnome/SessionManager")
        self.__session_manager_iface = dbus.Interface(session_manager,
            dbus_interface="org.gnome.SessionManager")

        # example: diplays the logout dialog
        #session_manager_iface.Logout(0)

        # registers a client with the session manager
        self.__client_id = self.__session_manager_iface.RegisterClient("", "")
        #self.logger.debug("Gnome Session Manager Client ID = %s" % self.__client_id)

        # create the proxy session manager client objects for calling methods
        session_client = session_bus.get_object("org.gnome.SessionManager",
                                                self.__client_id)
        self.__session_client_private_iface = dbus.Interface(session_client,
                    dbus_interface="org.gnome.SessionManager.ClientPrivate")

        # Introspectable interfaces define a property 'Introspect' that
        # will return an XML string that describes the object's interface
        #introspection_iface = dbus.Interface(
        #    session_client,
        #    dbus.INTROSPECTABLE_IFACE,
        #)
        #self.logger.debug(introspection_iface.Introspect())
        # NOTE: The org.gnome.SessionManager.ClientPrivate is not displayed for Introspect() calls

        session_bus.add_signal_receiver(self.__query_end_session_handler,
        	signal_name = "QueryEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__end_session_handler,
        	signal_name = "EndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__cancel_end_session_handler,
        	signal_name = "CancelEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__stop_handler,
        	signal_name = "Stop",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        self.logger.info("Initialized")

    def __teardown(self):
        self.__loop.quit()
        self.__session_manager_iface.UnregisterClient(self.__client_id)
        self.logger.info("Terminated DBus connections")

    #
    # Handle Unix signals
    #
    def __signal_handler(self, signum, stack):
        self.logger.info("Received shell signal: %s" % signum)
	# cannot call end_session_actions here as this may include
        # subprocess.call() or equivalent that is not permitted during signal handling
        # delay execution of the actions until executing the main thread
        self.interrupted = True
        self.__teardown()

    #
    # Methods defined in org.gnome.SessionManager.ClientPrivate
    #
    def __end_session_response(self, ok=True):
        if ok:
            self.logger.info("Calling DBus method:  EndSessionResponse(True, \"\")")
            self.__session_client_private_iface.EndSessionResponse(True, "")
        else:
            # NOTE: calling EndSessionResponse(False) does not inhibit logout on Ubuntu 14.10...
            self.logger.info("Calling DBus method:  EndSessionResponse(False, \"Not ready\")")
            self.__session_client_private_iface.EndSessionResponse(False, "Not ready")

    #
    # Signals defined in org.gnome.SessionManager.ClientPrivate
    # flags defined in GsmClientEndSessionFlag
    #       https://git.gnome.org/browse/gnome-session/tree/gnome-session/gsm-client.h
    # flags == 0: normal logout
    # flags == 1: Forced logout and content of EndSessionResponse will be ignored
    #
    def __query_end_session_handler(self, flags):
        self.logger.info("Received DBus signal: QueryEndSession(%d)" % flags)
        # ignore flags, always agree on the QueryEndSesion
        self.__end_session_response(True)

    def __end_session_handler(self, flags):
        self.logger.info("Received DBus signal: EndSession(%d)" % flags)
        self.end_session_actions()
        self.__end_session_response(True)

    def __cancel_end_session_handler(self):
        self.logger.info("Received DBus signal: CancelEndSession")
        #NOTE: ignored for now, i.e. known limitation

    def __stop_handler(self):
        self.logger.info("Received DBus signal: Stop")
        self.__teardown()

#
# Gnome End Session Listener
#
class GEndSessionListener(GEndSessionListenerBase):

    def __init__(self, cmdline):
        self.cmdline = cmdline
        super(GEndSessionListener, self).__init__()

    def end_session_actions(self):
        self.logger.debug("Performing user specified logout actions (%s)" % self.cmdline)

        cmd = self.cmdline[0]
        if not os.path.isabs(cmd):
            self.cmdline.pop(0)
            self.cmdline.insert(0, "./%s" % cmd)

        # NOTE: returncode is not checked as EndSessionResponse(False) does not
        # appear to abort the logout process.
        subprocess.call(self.cmdline)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: %s <end-session-script> [end-session-script-args]" % sys.argv[0])
        print()
        print("          end-session-script refers to the path to the executable script ")
        print("          to be run when the Gnome Session ends.")
        print()
        exit(1)

    cmdline = sys.argv[1:]
    #if not (os.path.isfile(end_session_script) and os.access(end_session_script, os.X_OK)):
    if not (os.access(cmdline[0], os.F_OK) and os.access(cmdline[0], os.X_OK)):
        print("The file '%s'  does not exist or is not an executable. Abort." % cmdline[0])
        exit(1)

    listener = GEndSessionListener(cmdline)
    listener.start()
<|endoftext|>


<commit_before># Copyright 2015 Richard Campen
# All rights reserved
# This software is released under the Modified BSD license
# See LICENSE.txt for the full license documentation

"""Script that parses output csv files generated by the Perkin Elmer EnSpire
2300 multilabel plate reader.

enparser version 0.1a
=====================

For complete documentation see README.rst.
"""

import pandas as pd
import csv

def parse_csv(file):
    """Extract individual plate data from EnSpire output csv file.

    Opens file using csv.reader and iterates through the file, returning an iterator
    of plate data sets as Pandas DataFrame objects. Plate data set dimensions are
    automatically determined using the dimensions of the first data set in each file.
    """

    header_text = [["Calculated results: Calc 1: Average within well = "
                    "Average of repeat measurements where Measurement: Meas A"],
                   ["Calculated results: Calc 2: Avg = "
                    "Average of the scanning points where Measurement : Meas A"]]

    with open(file, "r") as in_handle:
        raw_data = list(csv.reader(in_handle))

    # Auto detect plate size if not specified in function call arguments.
    data = enumerate(raw_data)
    first_header_index = next(index for index, row in data if row in header_text)
    # Subtract 2, 1 for each start of line letter end of line empty string
    num_columns = next(len(row) for index, row in data) - 2
    # Empty list follows last row of plate (Empty lists evaluate to False).
    last_plate_row = next(index for index, row in data if not row) - 1
    # Subtract 1 to account for row containing column headers.
    num_rows = (last_plate_row - first_header_index) - 1

    raw_data_sets = [pd.DataFrame(raw_data[index + 2: index + (num_rows + 2)])
                     for index, row in enumerate(raw_data) if row in header_text]

    trimmed_data_sets = []
    for raw_data_set in raw_data_sets:
        # Subtract 1 to account for column 0 which contains the row names
        data_set = raw_data_set.set_index(0)
        trimmed_data_set = data_set.iloc[0: num_rows, 0: num_columns]
        trimmed_data_sets.append(trimmed_data_set)

    return iter(trimmed_data_sets)

<commit_msg>Fix version number<commit_after># Copyright 2015 Richard Campen
# All rights reserved
# This software is released under the Modified BSD license
# See LICENSE.txt for the full license documentation

"""Script that parses output csv files generated by the Perkin Elmer EnSpire
2300 multilabel plate reader.

enparser version 0.1a1
======================

For complete documentation see README.rst.
"""

import pandas as pd
import csv

def parse_csv(file):
    """Extract individual plate data from EnSpire output csv file.

    Opens file using csv.reader and iterates through the file, returning an iterator
    of plate data sets as Pandas DataFrame objects. Plate data set dimensions are
    automatically determined using the dimensions of the first data set in each file.
    """

    header_text = [["Calculated results: Calc 1: Average within well = "
                    "Average of repeat measurements where Measurement: Meas A"],
                   ["Calculated results: Calc 2: Avg = "
                    "Average of the scanning points where Measurement : Meas A"]]

    with open(file, "r") as in_handle:
        raw_data = list(csv.reader(in_handle))

    # Auto detect plate size if not specified in function call arguments.
    data = enumerate(raw_data)
    first_header_index = next(index for index, row in data if row in header_text)
    # Subtract 2, 1 for each start of line letter end of line empty string
    num_columns = next(len(row) for index, row in data) - 2
    # Empty list follows last row of plate (Empty lists evaluate to False).
    last_plate_row = next(index for index, row in data if not row) - 1
    # Subtract 1 to account for row containing column headers.
    num_rows = (last_plate_row - first_header_index) - 1

    raw_data_sets = [pd.DataFrame(raw_data[index + 2: index + (num_rows + 2)])
                     for index, row in enumerate(raw_data) if row in header_text]

    trimmed_data_sets = []
    for raw_data_set in raw_data_sets:
        # Subtract 1 to account for column 0 which contains the row names
        data_set = raw_data_set.set_index(0)
        trimmed_data_set = data_set.iloc[0: num_rows, 0: num_columns]
        trimmed_data_sets.append(trimmed_data_set)

    return iter(trimmed_data_sets)

<|endoftext|>


<commit_before>import datetime
import time
import pytz

from boto.dynamodb2.fields import HashKey, RangeKey, AllIndex
from boto.dynamodb2.table import Table
from boto.dynamodb2.types import NUMBER

AWS_REGION = "us-east-1"
TABLE_NAME = "monitor_logs"
LOCAL_TIMEZONE = pytz.timezone("EST")

ENTRY_TYPE_HUMIDITY = "humidity"
ENTRY_TYPE_TEMPERATURE = "temperature"
ENTRY_TYPE_UV = "uv"
ENTRY_TYPE_LUMINANCE = "luminance"
ENTRY_TYPE_LOUDNESS = "loudness"

ENTRY_TYPES = (
    (ENTRY_TYPE_UV, "UV (mV)"),
    (ENTRY_TYPE_LUMINANCE, "Luminance (V)"),
    (ENTRY_TYPE_LOUDNESS, "Loudness (dB)"),
    (ENTRY_TYPE_HUMIDITY, "Humidity (%)"),
    (ENTRY_TYPE_TEMPERATURE, "Temperature (Celcius)"),
)

class DatabaseManager(object):
    """
    Database manager layer for DynamoDB
    """
    def __init__(self, connection, debug=False):
        super(DatabaseManager, self).__init__()

        self.connection = connection

        tables_list = self.connection.list_tables()

        if TABLE_NAME in tables_list.get("TableNames"):
            self.table = Table(table_name=TABLE_NAME, connection=self.connection)
        else:
            self.table = Table.create(TABLE_NAME, [
                HashKey("entry_type"),
                RangeKey("date_created", data_type=NUMBER)
            ], indexes=[
                AllIndex("DateJoinedIndex", parts=[
                    HashKey("entry_type"),
                    RangeKey("date_created", data_type=NUMBER)
                ])
            ], connection=self.connection)

class Reading(object):
    """
    A model for handling reading entries
    """
    def __init__(self, db_reading):
        super(Reading, self).__init__()

        self.entry_type = db_reading.get("entry_type")
        self.timestamp = db_reading.get("date_created")
        self.reading = db_reading.get("reading")

        naive_date = datetime.datetime.fromtimestamp(self.timestamp)
        utc_date = pytz.utc.localize(naive_date)
        local_date = utc_date.astimezone(LOCAL_TIMEZONE)

        self.date_created = local_date.strftime("%b %d %H:%M")

    def is_valid(self):
        """
        Checks if the reading value is valid
        """
        if self.reading is None:
            return False

        if self.entry_type == ENTRY_TYPE_HUMIDITY:
            return (float(self.reading) > 10)
        elif self.entry_type == ENTRY_TYPE_LUMINANCE:
            return (float(self.reading) > 0)
        elif self.entry_type == ENTRY_TYPE_LOUDNESS:
            return (float(self.reading) > 0)
        elif self.entry_type == ENTRY_TYPE_UV:
            return (float(self.reading) > 150)
        elif self.entry_type == ENTRY_TYPE_TEMPERATURE:
            return (float(self.reading) > -10)

        return True
<commit_msg>Add better validation for temperatures<commit_after>import datetime
import time
import pytz

from boto.dynamodb2.fields import HashKey, RangeKey, AllIndex
from boto.dynamodb2.table import Table
from boto.dynamodb2.types import NUMBER

AWS_REGION = "us-east-1"
TABLE_NAME = "monitor_logs"
LOCAL_TIMEZONE = pytz.timezone("EST")

ENTRY_TYPE_HUMIDITY = "humidity"
ENTRY_TYPE_TEMPERATURE = "temperature"
ENTRY_TYPE_UV = "uv"
ENTRY_TYPE_LUMINANCE = "luminance"
ENTRY_TYPE_LOUDNESS = "loudness"

ENTRY_TYPES = (
    (ENTRY_TYPE_UV, "UV (mV)"),
    (ENTRY_TYPE_LUMINANCE, "Luminance (V)"),
    (ENTRY_TYPE_LOUDNESS, "Loudness (dB)"),
    (ENTRY_TYPE_HUMIDITY, "Humidity (%)"),
    (ENTRY_TYPE_TEMPERATURE, "Temperature (Celcius)"),
)

class DatabaseManager(object):
    """
    Database manager layer for DynamoDB
    """
    def __init__(self, connection, debug=False):
        super(DatabaseManager, self).__init__()

        self.connection = connection

        tables_list = self.connection.list_tables()

        if TABLE_NAME in tables_list.get("TableNames"):
            self.table = Table(table_name=TABLE_NAME, connection=self.connection)
        else:
            self.table = Table.create(TABLE_NAME, [
                HashKey("entry_type"),
                RangeKey("date_created", data_type=NUMBER)
            ], indexes=[
                AllIndex("DateJoinedIndex", parts=[
                    HashKey("entry_type"),
                    RangeKey("date_created", data_type=NUMBER)
                ])
            ], connection=self.connection)

class Reading(object):
    """
    A model for handling reading entries
    """
    def __init__(self, db_reading):
        super(Reading, self).__init__()

        self.entry_type = db_reading.get("entry_type")
        self.timestamp = db_reading.get("date_created")
        self.reading = db_reading.get("reading")

        naive_date = datetime.datetime.fromtimestamp(self.timestamp)
        utc_date = pytz.utc.localize(naive_date)
        local_date = utc_date.astimezone(LOCAL_TIMEZONE)

        self.date_created = local_date.strftime("%b %d %H:%M")

    def is_valid(self):
        """
        Checks if the reading value is valid
        """
        if self.reading is None:
            return False

        if self.entry_type == ENTRY_TYPE_HUMIDITY:
            return (float(self.reading) > 10)
        elif self.entry_type == ENTRY_TYPE_LUMINANCE:
            return (float(self.reading) > 0)
        elif self.entry_type == ENTRY_TYPE_LOUDNESS:
            return (float(self.reading) > 0)
        elif self.entry_type == ENTRY_TYPE_UV:
            return (float(self.reading) > 150)
        elif self.entry_type == ENTRY_TYPE_TEMPERATURE:
            return (float(self.reading) > -10 and float(self.reading) < 50)

        return True
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) 2014, Nicolas P. Rougier
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
"""
Default argument parser for any glumpy program.
"""
import argparse
import glumpy.defaults

# Default parser
__parser__ = None

def get_default():
    """ Get the default parser. """

    global __parser__

    if __parser__ is None:
        __parser__ = argparse.ArgumentParser()
        set_default_options(__parser__)
    return __parser__

def get_options():
    """ Parse and retrun command line options. """

    return get_default().parse_args()

def set_default_options(parser):
    """ Set parser default options. """

    # Backend option
    parser.add_argument("--backend", "-b",
                        default = glumpy.defaults.backend(),
                        choices = ('glfw', 'sdl2', 'pyside', 'pyglet', 'sdl',  'osxglut'),
                        help="Backend to use, one of ")

    # Record
    parser.add_argument("--record",
                        action='store_true',
                        help='Record a movie (default is "movie.mp4")')

    # Interactive mode
    parser.add_argument("--interactive", "-i",
                        action='store_true',
                        help="Interactive mode")

    # Framerate option
    parser.add_argument("--framerate", "-f",
                        default=60,
                        type=int,
                        help="Framerate in frames/second")

    # Display framerate option
    parser.add_argument("--display-fps",
                        action='store_true',
                        help="Display framerate in the console")

    # Framerate option
    parser.add_argument("--debug", "-d",
                        action='store_true',
                        help="Verbose debug mode")

    # Window size
    parser.add_argument("--size", "-s",
                        default = "",
                        type=str,
                        help="Window size")

    # Window position
    parser.add_argument("--position", "-p",
                        default = "",
                        type=str,
                        help="Window position")

    # Single buffer
    parser.add_argument("--single-buffer",
                        action='store_true',
                        help="Single buffer mode")

    # Stereo mode
    parser.add_argument("--stereo",
                        action='store_true',
                        help="Stereo mode")

    # vertical synchronization
    parser.add_argument("--vsync",
                        default=False,
                        type=bool,
                        help="Enable/disable vertical synchronization")

    # sRGB mode
    parser.add_argument("--srgb",
                        action='store_true',
                        help="sRGB mode (gamma correction)")

    # Depth buffer size
    parser.add_argument("--depth-size",
                        default=16,
                        type=int,
                        help="Depth buffer size")

    # Stencil buffer size
    parser.add_argument("--stencil-size",
                        default=0,
                        type=int,
                        help="Stencil buffer size")

    # GL API
    parser.add_argument("--gl-api",
                        default="GL",
                        choices=["GL","ES"],
                        help="GL API")

    # GL profile
    parser.add_argument("--gl-profile",
                        default="none",
                        choices=["none","core", "compatibility"],
                        help="GL context profile (only relevant for GL > 3.0)")

    # GL version
    parser.add_argument("--gl-version",
                        default="2.1",
                        help="GL version")
<commit_msg>Inability to parse arguments should not result in a failure<commit_after># -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) 2014, Nicolas P. Rougier
# Distributed under the (new) BSD License. See LICENSE.txt for more info.
# -----------------------------------------------------------------------------
"""
Default argument parser for any glumpy program.
"""
import argparse
import glumpy.defaults

# Default parser
__parser__ = None

def get_default():
    """ Get the default parser. """

    global __parser__

    if __parser__ is None:
        __parser__ = argparse.ArgumentParser()
        set_default_options(__parser__)
    return __parser__

def get_options():
    """ Parse and retrun command line options. """

    options, unknown = get_default().parse_known_args()
    return options

def set_default_options(parser):
    """ Set parser default options. """

    # Backend option
    parser.add_argument("--backend", "-b",
                        default = glumpy.defaults.backend(),
                        choices = ('glfw', 'sdl2', 'pyside', 'pyglet', 'sdl',  'osxglut'),
                        help="Backend to use, one of ")

    # Record
    parser.add_argument("--record",
                        action='store_true',
                        help='Record a movie (default is "movie.mp4")')

    # Interactive mode
    parser.add_argument("--interactive", "-i",
                        action='store_true',
                        help="Interactive mode")

    # Framerate option
    parser.add_argument("--framerate", "-f",
                        default=60,
                        type=int,
                        help="Framerate in frames/second")

    # Display framerate option
    parser.add_argument("--display-fps",
                        action='store_true',
                        help="Display framerate in the console")

    # Framerate option
    parser.add_argument("--debug", "-d",
                        action='store_true',
                        help="Verbose debug mode")

    # Window size
    parser.add_argument("--size", "-s",
                        default = "",
                        type=str,
                        help="Window size")

    # Window position
    parser.add_argument("--position", "-p",
                        default = "",
                        type=str,
                        help="Window position")

    # Single buffer
    parser.add_argument("--single-buffer",
                        action='store_true',
                        help="Single buffer mode")

    # Stereo mode
    parser.add_argument("--stereo",
                        action='store_true',
                        help="Stereo mode")

    # vertical synchronization
    parser.add_argument("--vsync",
                        default=False,
                        type=bool,
                        help="Enable/disable vertical synchronization")

    # sRGB mode
    parser.add_argument("--srgb",
                        action='store_true',
                        help="sRGB mode (gamma correction)")

    # Depth buffer size
    parser.add_argument("--depth-size",
                        default=16,
                        type=int,
                        help="Depth buffer size")

    # Stencil buffer size
    parser.add_argument("--stencil-size",
                        default=0,
                        type=int,
                        help="Stencil buffer size")

    # GL API
    parser.add_argument("--gl-api",
                        default="GL",
                        choices=["GL","ES"],
                        help="GL API")

    # GL profile
    parser.add_argument("--gl-profile",
                        default="none",
                        choices=["none","core", "compatibility"],
                        help="GL context profile (only relevant for GL > 3.0)")

    # GL version
    parser.add_argument("--gl-version",
                        default="2.1",
                        help="GL version")
<|endoftext|>


<commit_before>#!/usr/bin/python

#
# The MIT License (MIT)
#
# Copyright (c) 2015 Pablo Cases
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

import sys
import os
from dbus.mainloop.glib import DBusGMainLoop
import dbus
import gobject
import datetime
import signal
import subprocess
import logging
import logging.handlers
import inspect

class GEndSessionListenerBase(object):

    # override this method when subclassing
    # this method contains the actions performed during logout/"Gnome EndSession"
    def end_session_actions(self):
        # According to Gnome Session Manager docs:
        # "The client must not attempt to interact with the user [...].
        # The application will be given a maxium of ten seconds to perform
        # any actions required for a clean shutdown."
        raise NotImplementedError()

    # start() is a blocking method and returns only
    # during logout or if interrupted by a Unix signal
    def start(self):
	# signals can only be received while the event loop is running
        self.__loop = gobject.MainLoop()
        self.__loop.run()

        # run actions when interrupted by a Unix signal
        if self.interrupted:
            self.end_session_actions()

        self.logger.info("Terminated")

    def __init__(self):
        # will be set to True if a Unix signal is caught and handled
        self.interrupted = False

        # handle Unix signals
        #  (SIGKILL and SIGSTOP cannot be caught, blocked, or ignored)
        signal.signal(signal.SIGHUP, self.__signal_handler)
        signal.signal(signal.SIGINT, self.__signal_handler) # ctrl-c
        signal.signal(signal.SIGQUIT, self.__signal_handler) # ctrl-\
        signal.signal(signal.SIGTERM, self.__signal_handler)
        signal.signal(signal.SIGUSR1, signal.SIG_IGN)
        signal.signal(signal.SIGUSR2, signal.SIG_IGN)
        signal.signal(signal.SIGTSTP, self.__signal_handler) # ctrl-z
        # the remaining signals are considered fatal and are left unhandled

        # create the logger object
        FORMAT = os.path.basename(inspect.getfile(self.__class__)) + ": %(message)s"
        self.logger = logging.getLogger()
        formatter = logging.Formatter(FORMAT)
        handler = logging.handlers.SysLogHandler(address = "/dev/log")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)

        # must be setup to receive signals
        DBusGMainLoop(set_as_default=True)

        # connect to the appropriate bus
        session_bus = dbus.SessionBus()

        # create the proxy session manager objects for calling methods
        session_manager = session_bus.get_object("org.gnome.SessionManager",
        					 "/org/gnome/SessionManager")
        self.__session_manager_iface = dbus.Interface(session_manager,
            dbus_interface="org.gnome.SessionManager")

        # example: diplays the logout dialog
        #session_manager_iface.Logout(0)

        # registers a client with the session manager
        self.__client_id = self.__session_manager_iface.RegisterClient("", "")
        #self.logger.debug("Gnome Session Manager Client ID = %s" % self.__client_id)

        # create the proxy session manager client objects for calling methods
        session_client = session_bus.get_object("org.gnome.SessionManager",
                                                self.__client_id)
        self.__session_client_private_iface = dbus.Interface(session_client,
                    dbus_interface="org.gnome.SessionManager.ClientPrivate")

        # Introspectable interfaces define a property 'Introspect' that
        # will return an XML string that describes the object's interface
        #introspection_iface = dbus.Interface(
        #    session_client,
        #    dbus.INTROSPECTABLE_IFACE,
        #)
        #self.logger.debug(introspection_iface.Introspect())
        # NOTE: The org.gnome.SessionManager.ClientPrivate is not displayed for Introspect() calls

        session_bus.add_signal_receiver(self.__query_end_session_handler,
        	signal_name = "QueryEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__end_session_handler,
        	signal_name = "EndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__cancel_end_session_handler,
        	signal_name = "CancelEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__stop_handler,
        	signal_name = "Stop",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        self.logger.info("Initialized")

    def __teardown(self):
        self.__loop.quit()
        self.__session_manager_iface.UnregisterClient(self.__client_id)
        self.logger.info("Terminated DBus connections")

    #
    # Handle Unix signals
    #
    def __signal_handler(self, signum, stack):
        self.logger.info("Received shell signal: %s" % signum)
	# cannot call end_session_actions here as this may include
        # subprocess.call() or equivalent that is not permitted during signal handling
        # delay execution of the actions until executing the main thread
        self.interrupted = True
        self.__teardown()

    #
    # Methods defined in org.gnome.SessionManager.ClientPrivate
    #
    def __end_session_response(self, ok=True):
        if ok:
            self.logger.info("Calling DBus method:  EndSessionResponse(True, \"\")")
            self.__session_client_private_iface.EndSessionResponse(True, "")
        else:
            # NOTE: calling EndSessionResponse(False) does not inhibit logout on Ubuntu 14.10...
            self.logger.info("Calling DBus method:  EndSessionResponse(False, \"Not ready\")")
            self.__session_client_private_iface.EndSessionResponse(False, "Not ready")

    #
    # Signals defined in org.gnome.SessionManager.ClientPrivate
    # flags defined in GsmClientEndSessionFlag
    #       https://git.gnome.org/browse/gnome-session/tree/gnome-session/gsm-client.h
    # flags == 0: normal logout
    # flags == 1: Forced logout and content of EndSessionResponse will be ignored
    #
    def __query_end_session_handler(self, flags):
        self.logger.info("Received DBus signal: QueryEndSession(%d)" % flags)
        # ignore flags, always agree on the QueryEndSesion
        self.__end_session_response(True)

    def __end_session_handler(self, flags):
        self.logger.info("Received DBus signal: EndSession(%d)" % flags)
        self.end_session_actions()
        self.__end_session_response(True)

    def __cancel_end_session_handler(self):
        self.logger.info("Received DBus signal: CancelEndSession")
        #NOTE: ignored for now, i.e. known limitation

    def __stop_handler(self):
        self.logger.info("Received DBus signal: Stop")
        self.__teardown()

#
# Gnome End Session Listener
#
class GEndSessionListener(GEndSessionListenerBase):

    def __init__(self, cmdline):
        self.cmdline = cmdline
        super(GEndSessionListener, self).__init__()

    def end_session_actions(self):
        self.logger.debug("Performing user specified logout actions (%s)" % self.cmdline)

	cmd = self.cmdline[0]
        if not os.path.isabs(cmd):
            self.cmdline.pop(0)
            self.cmdline.insert(0, "./%s" % cmd)

        # NOTE: returncode is not checked as EndSessionResponse(False) does not
        # appear to abort the logout process.
        subprocess.call(self.cmdline)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "Usage: %s <end-session-script> [end-session-script-args]" % sys.argv[0]
        print
        print "          end-session-script refers to the path to the executable script "
        print "          to be run when the Gnome Session ends."
        print
        exit(1)

    cmdline = sys.argv[1:]
    #if not (os.path.isfile(end_session_script) and os.access(end_session_script, os.X_OK)):
    if not (os.access(cmdline[0], os.F_OK) and os.access(cmdline[0], os.X_OK)):
        print "The file '%s'  does not exist or is not an executable. Abort." % cmdline[0]
        exit(1)

    listener = GEndSessionListener(cmdline)
    listener.start()
<commit_msg>Shortened license header<commit_after>#!/usr/bin/python

#
# Copyright (c) 2015 Pablo Cases,
# Distributed under the MIT License.
# (See accompanying file LICENSE file or
# copy at http://opensource.org/licenses/MIT)
#

import sys
import os
from dbus.mainloop.glib import DBusGMainLoop
import dbus
import gobject
import datetime
import signal
import subprocess
import logging
import logging.handlers
import inspect

class GEndSessionListenerBase(object):

    # override this method when subclassing
    # this method contains the actions performed during logout/"Gnome EndSession"
    def end_session_actions(self):
        # According to Gnome Session Manager docs:
        # "The client must not attempt to interact with the user [...].
        # The application will be given a maxium of ten seconds to perform
        # any actions required for a clean shutdown."
        raise NotImplementedError()

    # start() is a blocking method and returns only
    # during logout or if interrupted by a Unix signal
    def start(self):
	# signals can only be received while the event loop is running
        self.__loop = gobject.MainLoop()
        self.__loop.run()

        # run actions when interrupted by a Unix signal
        if self.interrupted:
            self.end_session_actions()

        self.logger.info("Terminated")

    def __init__(self):
        # will be set to True if a Unix signal is caught and handled
        self.interrupted = False

        # handle Unix signals
        #  (SIGKILL and SIGSTOP cannot be caught, blocked, or ignored)
        signal.signal(signal.SIGHUP, self.__signal_handler)
        signal.signal(signal.SIGINT, self.__signal_handler) # ctrl-c
        signal.signal(signal.SIGQUIT, self.__signal_handler) # ctrl-\
        signal.signal(signal.SIGTERM, self.__signal_handler)
        signal.signal(signal.SIGUSR1, signal.SIG_IGN)
        signal.signal(signal.SIGUSR2, signal.SIG_IGN)
        signal.signal(signal.SIGTSTP, self.__signal_handler) # ctrl-z
        # the remaining signals are considered fatal and are left unhandled

        # create the logger object
        FORMAT = os.path.basename(inspect.getfile(self.__class__)) + ": %(message)s"
        self.logger = logging.getLogger()
        formatter = logging.Formatter(FORMAT)
        handler = logging.handlers.SysLogHandler(address = "/dev/log")
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
        self.logger.setLevel(logging.DEBUG)

        # must be setup to receive signals
        DBusGMainLoop(set_as_default=True)

        # connect to the appropriate bus
        session_bus = dbus.SessionBus()

        # create the proxy session manager objects for calling methods
        session_manager = session_bus.get_object("org.gnome.SessionManager",
        					 "/org/gnome/SessionManager")
        self.__session_manager_iface = dbus.Interface(session_manager,
            dbus_interface="org.gnome.SessionManager")

        # example: diplays the logout dialog
        #session_manager_iface.Logout(0)

        # registers a client with the session manager
        self.__client_id = self.__session_manager_iface.RegisterClient("", "")
        #self.logger.debug("Gnome Session Manager Client ID = %s" % self.__client_id)

        # create the proxy session manager client objects for calling methods
        session_client = session_bus.get_object("org.gnome.SessionManager",
                                                self.__client_id)
        self.__session_client_private_iface = dbus.Interface(session_client,
                    dbus_interface="org.gnome.SessionManager.ClientPrivate")

        # Introspectable interfaces define a property 'Introspect' that
        # will return an XML string that describes the object's interface
        #introspection_iface = dbus.Interface(
        #    session_client,
        #    dbus.INTROSPECTABLE_IFACE,
        #)
        #self.logger.debug(introspection_iface.Introspect())
        # NOTE: The org.gnome.SessionManager.ClientPrivate is not displayed for Introspect() calls

        session_bus.add_signal_receiver(self.__query_end_session_handler,
        	signal_name = "QueryEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__end_session_handler,
        	signal_name = "EndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__cancel_end_session_handler,
        	signal_name = "CancelEndSession",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        session_bus.add_signal_receiver(self.__stop_handler,
        	signal_name = "Stop",
        	dbus_interface = "org.gnome.SessionManager.ClientPrivate",
        	bus_name = "org.gnome.SessionManager")

        self.logger.info("Initialized")

    def __teardown(self):
        self.__loop.quit()
        self.__session_manager_iface.UnregisterClient(self.__client_id)
        self.logger.info("Terminated DBus connections")

    #
    # Handle Unix signals
    #
    def __signal_handler(self, signum, stack):
        self.logger.info("Received shell signal: %s" % signum)
	# cannot call end_session_actions here as this may include
        # subprocess.call() or equivalent that is not permitted during signal handling
        # delay execution of the actions until executing the main thread
        self.interrupted = True
        self.__teardown()

    #
    # Methods defined in org.gnome.SessionManager.ClientPrivate
    #
    def __end_session_response(self, ok=True):
        if ok:
            self.logger.info("Calling DBus method:  EndSessionResponse(True, \"\")")
            self.__session_client_private_iface.EndSessionResponse(True, "")
        else:
            # NOTE: calling EndSessionResponse(False) does not inhibit logout on Ubuntu 14.10...
            self.logger.info("Calling DBus method:  EndSessionResponse(False, \"Not ready\")")
            self.__session_client_private_iface.EndSessionResponse(False, "Not ready")

    #
    # Signals defined in org.gnome.SessionManager.ClientPrivate
    # flags defined in GsmClientEndSessionFlag
    #       https://git.gnome.org/browse/gnome-session/tree/gnome-session/gsm-client.h
    # flags == 0: normal logout
    # flags == 1: Forced logout and content of EndSessionResponse will be ignored
    #
    def __query_end_session_handler(self, flags):
        self.logger.info("Received DBus signal: QueryEndSession(%d)" % flags)
        # ignore flags, always agree on the QueryEndSesion
        self.__end_session_response(True)

    def __end_session_handler(self, flags):
        self.logger.info("Received DBus signal: EndSession(%d)" % flags)
        self.end_session_actions()
        self.__end_session_response(True)

    def __cancel_end_session_handler(self):
        self.logger.info("Received DBus signal: CancelEndSession")
        #NOTE: ignored for now, i.e. known limitation

    def __stop_handler(self):
        self.logger.info("Received DBus signal: Stop")
        self.__teardown()

#
# Gnome End Session Listener
#
class GEndSessionListener(GEndSessionListenerBase):

    def __init__(self, cmdline):
        self.cmdline = cmdline
        super(GEndSessionListener, self).__init__()

    def end_session_actions(self):
        self.logger.debug("Performing user specified logout actions (%s)" % self.cmdline)

	cmd = self.cmdline[0]
        if not os.path.isabs(cmd):
            self.cmdline.pop(0)
            self.cmdline.insert(0, "./%s" % cmd)

        # NOTE: returncode is not checked as EndSessionResponse(False) does not
        # appear to abort the logout process.
        subprocess.call(self.cmdline)

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print "Usage: %s <end-session-script> [end-session-script-args]" % sys.argv[0]
        print
        print "          end-session-script refers to the path to the executable script "
        print "          to be run when the Gnome Session ends."
        print
        exit(1)

    cmdline = sys.argv[1:]
    #if not (os.path.isfile(end_session_script) and os.access(end_session_script, os.X_OK)):
    if not (os.access(cmdline[0], os.F_OK) and os.access(cmdline[0], os.X_OK)):
        print "The file '%s'  does not exist or is not an executable. Abort." % cmdline[0]
        exit(1)

    listener = GEndSessionListener(cmdline)
    listener.start()
<|endoftext|>