# -*- coding: utf-8 -*-
""" Panohead remote control.
License
=======
- B{Papywizard} (U{http://www.papywizard.org}) is Copyright:
- (C) 2007-2010 Frédéric Mantegazza
This software is governed by the B{CeCILL} license under French law and
abiding by the rules of distribution of free software. You can use,
modify and/or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
U{http://www.cecill.info}.
As a counterpart to the access to the source code and rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty and the software's author, the holder of the
economic rights, and the successive licensors have only limited
liability.
In this respect, the user's attention is drawn to the risks associated
with loading, using, modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean that it is complicated to manipulate, and that also
therefore means that it is reserved for developers and experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or
data to be ensured and, more generally, to use and operate it in the
same conditions as regards security.
The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.
Module purpose
==============
Plugins
Implements
==========
- GphotoBracketShutter
- GphotoBracketShutterController
@author: Jeongyun Lee
@author: Frédéric Mantegazza
@copyright: (C) 2010 Jeongyun Lee
@copyright: (C) 2007-2010 Frédéric Mantegazza
@license: CeCILL
"""
__revision__ = ""
import time
import subprocess
from PyQt4 import QtCore, QtGui
from papywizard.common.loggingServices import Logger
from papywizard.plugins.pluginsManager import PluginsManager
from papywizard.plugins.abstractShutterPlugin import AbstractShutterPlugin
from papywizard.plugins.shutterPluginController import ShutterPluginController
from papywizard.view.pluginFields import ComboBoxField, LineEditField, SpinBoxField, DoubleSpinBoxField, \
CheckBoxField, SliderField, FileSelectorField
NAME = "Gphoto Bracket"
DEFAULT_MIRROR_LOCKUP = False
DEFAULT_MIRROR_LOCKUP_COMMAND = "gphoto2 --capture-image"
DEFAULT_SHOOT_COMMAND = "gphoto2 --capture-image"
DEFAULT_BRACKETING_NBPICTS = 1
DEFAULT_BRACKETING_STEP = 1.
LABEL_SHOOT_COMMAND = QtGui.QApplication.translate("gphotoBracketPlugins", "Shoot command")
LABEL_NB_PICTS = QtGui.QApplication.translate("gphotoBracketPlugins", "Bracketing # picts")
LABEL_EV_STEP = QtGui.QApplication.translate("gphotoBracketPlugins", "EV step")
LABEL_EV_LIST = QtGui.QApplication.translate("gphotoBracketPlugins", "Bracketing sequence")
LABEL_ADVANCED = QtGui.QApplication.translate("gphotoBracketPlugins", "Advanced")
LABEL_ADVANCED_TAB = QtGui.QApplication.translate("gphotoBracketPlugins", 'Advanced')
LABEL_PLUS_NB_PICTS = QtGui.QApplication.translate("gphotoBracketPlugins", "Plus(+) bracketing # picts")
LABEL_PLUS_STEP = QtGui.QApplication.translate("gphotoBracketPlugins", "+ step")
LABEL_MINUS_NB_PICTS = QtGui.QApplication.translate("gphotoBracketPlugins", "Minus(-) bracketing # picts")
LABEL_MINUS_STEP = QtGui.QApplication.translate("gphotoBracketPlugins", "- step")
class GphotoBracketShutter(AbstractShutterPlugin):
"""
"""
def _init(self):
pass
def _getTimeValue(self):
return -1
def _getMirrorLockup(self):
return self._config['MIRROR_LOCKUP']
def _getBracketingNbPicts(self):
return self._config['BRACKETING_NB_PICTS']
def _defineConfig(self):
Logger().trace("GphotoBracketShutter._defineConfig()")
#AbstractShutterPlugin._defineConfig(self)
self._addConfigKey('_mirrorLockup', 'MIRROR_LOCKUP', default=DEFAULT_MIRROR_LOCKUP)
self._addConfigKey('_mirrorLockupCommand', 'MIRROR_LOCKUP_COMMAND', default=DEFAULT_MIRROR_LOCKUP_COMMAND)
self._addConfigKey('_shootCommand', 'SHOOT_COMMAND', default=DEFAULT_SHOOT_COMMAND)
self._addConfigKey('_bracketingNbPicts', 'BRACKETING_NB_PICTS', default=DEFAULT_BRACKETING_NBPICTS)
self._addConfigKey('_bracketingEvStep', 'BRACKETING_EV_STEP', default=DEFAULT_BRACKETING_STEP)
self._addConfigKey('_bracketingPlusNbPicts', 'BRACKETING_PLUS_NB_PICTS', default=int(DEFAULT_BRACKETING_NBPICTS/2))
self._addConfigKey('_bracketingMinusNbPicts', 'BRACKETING_MINUS_NB_PICTS', default=int(DEFAULT_BRACKETING_NBPICTS/2))
self._addConfigKey('_bracketingPlusStep', 'BRACKETING_PLUS_STEP', default=DEFAULT_BRACKETING_STEP)
self._addConfigKey('_bracketingMinusStep', 'BRACKETING_MINUS_STEP', default=DEFAULT_BRACKETING_STEP)
self._addConfigKey('_bracketingEvList', 'BRACKETING_EV_LIST', default='')
self._addConfigKey('_bracketingAdvanced', 'BRACKETING_ADVANCED', default=False)
def lockupMirror(self):
# @todo: implement mirror lockup command
Logger().debug("GphotoBracketShutter.lockupMirror(): execute command '%s'..." % self._config['MIRROR_LOCKUP_COMMAND'])
time.sleep(1)
Logger().debug("GphotoBracketShutter.lockupMirror(): command over")
return 0
def init(self):
# gphoto2 --list-config
# Launch external command
args = [ self._config['SHOOT_COMMAND'].split()[0] ]
args.insert(1, "--list-config")
Logger().debug("GphotoBracketShutter.init(): execute command '%s'..." % ' '.join(args))
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Wait end of execution
stdout, stderr = p.communicate()
if p.returncode != 0 and stderr:
Logger().error("GphotoBracketShutter.init(): stderr:\n%s" % stderr.strip())
Logger().debug("GphotoBracketShutter.init(): stdout:\n%s" % stdout.strip())
for line in stdout.splitlines():
if line.endswith('/shutterspeed'):
self._speed_config = line
elif line.endswith('/exptime'):
self._speed_config = line
# gphoto2 --get-config
# Launch external command
args = [ self._config['SHOOT_COMMAND'].split()[0] ]
args.insert(1, "--get-config")
args.insert(2, self._speed_config)
Logger().debug("GphotoBracketShutter.init(): execute command '%s'..." % ' '.join(args))
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Wait end of execution
stdout, stderr = p.communicate()
if p.returncode != 0 and stderr:
Logger().error("GphotoBracketShutter.init(): stderr:\n%s" % stderr.strip())
Logger().debug("GphotoBracketShutter.init(): stdout:\n%s" % stdout.strip())
# Load all available speeds and the current speed
self._avail_speeds = []
for line in stdout.splitlines():
tokens = line.split()
if tokens[0] == 'Current:':
self._basespeed = tokens[1]
elif tokens[0] == 'Choice:':
self._avail_speeds.insert(int(tokens[1]), tokens[2])
if self._basespeed == tokens[2]:
self._basespeed_index = int(tokens[1])
if float(self._avail_speeds[1]) > float(self._avail_speeds[2]):
self._speed_order = 1.0 # slower speed first
else:
self._speed_order = -1.0 # faster speed first
# Guess EV step (1/2, or 1/3)
if float(self._avail_speeds[1])/float(self._avail_speeds[3]) < 1.9:
self._evsteps = 3
else:
self._evsteps = 2
Logger().info("GphotoBracketShutter.init(): basespeed: %s, config: %s, order: %+g, steps: 1/%d" % (self._basespeed, self._speed_config, self._speed_order, self._evsteps))
def _getEvOffset(self, bracketNumber):
# bracketNumber out of self._config['BRACKETING_NB_PICTS']
plusNbPicts = int(self._config['BRACKETING_PLUS_NB_PICTS'])
minusNbPicts = int(self._config['BRACKETING_MINUS_NB_PICTS'])
plusStep = self._config['BRACKETING_PLUS_STEP']
minusStep = self._config['BRACKETING_MINUS_STEP']
if bracketNumber <= minusNbPicts: # 1,2...minusNbPicts
return (bracketNumber - minusNbPicts - 1) * minusStep
elif bracketNumber >= (minusNbPicts+2): # minusNbPicts+2, +3...(minusNbPicts+plusNbPicts+1)
return (bracketNumber - minusNbPicts - 1) * plusStep
else:
return 0
def shoot(self, bracketNumber):
Logger().debug("GphotoBracketShutter.shoot(): bracketNumber=%d" % bracketNumber)
ev_offset = self._getEvOffset(bracketNumber)
speed_index = self._basespeed_index - int(ev_offset * self._evsteps * self._speed_order)
# see if shutter speed is out of range
if self._speed_order > 0: # slow speed first
if speed_index < 1:
speed_index = 1
elif speed_index >= len(self._avail_speeds):
speed_index = len(self._avail_speeds)-1
elif self._speed_order < 0: # fast speed first
if speed_index < 0:
speed_index = 0
elif speed_index >= (len(self._avail_speeds)-1):
speed_index = len(self._avail_speeds)-2
Logger().info("GphotoBracketShutter.shoot(): EV %+d, shutter speed: %s (1/%d steps)" % (int(ev_offset), self._avail_speeds[speed_index], self._evsteps))
# Launch external command
args = self._config['SHOOT_COMMAND'].split()
args.insert(1, "--set-config")
args.insert(2, "%s=%s" % (self._speed_config, self._avail_speeds[speed_index]))
args.append("--set-config")
args.append("%s=%s" % (self._speed_config, self._basespeed))
Logger().debug("GphotoBracketShutter.shoot(): execute command '%s'..." % ' '.join(args))
if True:
p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# Wait end of execution
stdout, stderr = p.communicate()
if p.returncode != 0 and stderr:
Logger().error("GphotoBracketShutter.shoot(): stderr:\n%s" % stderr.strip())
Logger().debug("GphotoBracketShutter.shoot(): stdout:\n%s" % stdout.strip())
return p.returncode
else:
return 0
class GphotoBracketShutterController(ShutterPluginController):
def _getWidget(self, tab, label):
return self._fields[tab][label]['widget']
def _updateAdvanced(self):
advanced = self._getWidget('Main', LABEL_ADVANCED).value()
self._getWidget('Main', LABEL_NB_PICTS).setDisabled(advanced)
self._getWidget('Main', LABEL_EV_STEP).setDisabled(advanced)
self._getWidget('Advanced', LABEL_PLUS_NB_PICTS).setDisabled(not advanced)
self._getWidget('Advanced', LABEL_MINUS_NB_PICTS).setDisabled(not advanced)
self._getWidget('Advanced', LABEL_PLUS_STEP).setDisabled(not advanced)
self._getWidget('Advanced', LABEL_MINUS_STEP).setDisabled(not advanced)
def _updateBracketingInfo(self,param):
self._updateAdvanced()
advanced = self._getWidget('Main', LABEL_ADVANCED).value()
if not advanced:
halfNbPicts = (self._getWidget('Main', LABEL_NB_PICTS).value()-1)/2
evStep = self._getWidget('Main', LABEL_EV_STEP).value()
self._getWidget('Advanced', LABEL_PLUS_NB_PICTS).setValue(halfNbPicts)
self._getWidget('Advanced', LABEL_MINUS_NB_PICTS).setValue(halfNbPicts)
self._getWidget('Advanced', LABEL_PLUS_STEP).setValue(evStep)
self._getWidget('Advanced', LABEL_MINUS_STEP).setValue(evStep)
plusNbPicts = int(self._getWidget('Advanced', LABEL_PLUS_NB_PICTS).value())
minusNbPicts = int(self._getWidget('Advanced', LABEL_MINUS_NB_PICTS).value())
plusStep = int(self._getWidget('Advanced', LABEL_PLUS_STEP).value())
minusStep = int(self._getWidget('Advanced', LABEL_MINUS_STEP).value())
self._getWidget('Main', LABEL_NB_PICTS).setValue(1 + plusNbPicts + minusNbPicts)
ev_list = [ ]
if minusNbPicts > 0:
for i in range(-minusNbPicts, 0):
ev_list.append("%+g" % (i * minusStep))
ev_list.append("0")
if plusNbPicts > 0:
for i in range(1, plusNbPicts+1):
ev_list.append("%+g" % (i * plusStep))
self._getWidget('Main', LABEL_EV_LIST).setValue(", ".join(ev_list))
self._getWidget('Advanced', LABEL_EV_LIST).setValue(", ".join(ev_list))
def _defineGui(self):
Logger().trace("GphotoBracketShutterController._defineGui()")
ShutterPluginController._defineGui(self)
#self._addWidget('Main', QtGui.QApplication.translate("gphotoBracketPlugins", "Mirror lockup"),
#CheckBoxField, (), 'MIRROR_LOCKUP')
#self._addWidget('Main', QtGui.QApplication.translate("gphotoBracketPlugins", "Mirror lockup command"),
#LineEditField, (), 'MIRROR_LOCKUP_COMMAND')
# Main tab
self._addWidget('Main', LABEL_SHOOT_COMMAND, LineEditField, (), 'SHOOT_COMMAND')
self._addWidget('Main', LABEL_NB_PICTS, SpinBoxField, (1, 11), 'BRACKETING_NB_PICTS')
self._getWidget('Main', LABEL_NB_PICTS).setSingleStep(2)
self._addWidget('Main', LABEL_EV_STEP, DoubleSpinBoxField, (1.0, 5.0, 1, 1.0, "", " ev"), 'BRACKETING_EV_STEP')
self._addWidget('Main', LABEL_EV_LIST, LineEditField, (), 'BRACKETING_EV_LIST')
self._getWidget('Main', LABEL_EV_LIST).setDisabled(True)
self._addWidget('Main', LABEL_ADVANCED, CheckBoxField, (), 'BRACKETING_ADVANCED')
# Advanced tab
self._addTab('Advanced', LABEL_ADVANCED_TAB)
self._addWidget('Advanced', LABEL_PLUS_NB_PICTS, SpinBoxField, (0, 11), 'BRACKETING_PLUS_NB_PICTS')
self._addWidget('Advanced', LABEL_PLUS_STEP, DoubleSpinBoxField, (1.0, 5.0, 1, 1.0, "", " ev"), 'BRACKETING_PLUS_STEP')
self._addWidget('Advanced', LABEL_MINUS_NB_PICTS, SpinBoxField, (0, 11), 'BRACKETING_MINUS_NB_PICTS')
self._addWidget('Advanced', LABEL_MINUS_STEP, DoubleSpinBoxField, (1.0, 5.0, 1, 1.0, "", " ev"), 'BRACKETING_MINUS_STEP')
self._addWidget('Advanced', LABEL_EV_LIST, LineEditField, (), 'BRACKETING_EV_LIST')
self._getWidget('Advanced', LABEL_EV_LIST).setDisabled(True)
self.connect(self._getWidget('Main', LABEL_NB_PICTS), QtCore.SIGNAL("valueChanged(int)"), self._updateBracketingInfo)
self.connect(self._getWidget('Main', LABEL_EV_STEP), QtCore.SIGNAL("valueChanged(double)"), self._updateBracketingInfo)
self.connect(self._getWidget('Main', LABEL_ADVANCED), QtCore.SIGNAL("stateChanged(int)"), self._updateBracketingInfo)
self.connect(self._getWidget('Advanced', LABEL_PLUS_NB_PICTS), QtCore.SIGNAL("valueChanged(int)"), self._updateBracketingInfo)
self.connect(self._getWidget('Advanced', LABEL_MINUS_NB_PICTS), QtCore.SIGNAL("valueChanged(int)"), self._updateBracketingInfo)
self.connect(self._getWidget('Advanced', LABEL_PLUS_STEP), QtCore.SIGNAL("valueChanged(double)"), self._updateBracketingInfo)
self.connect(self._getWidget('Advanced', LABEL_MINUS_STEP), QtCore.SIGNAL("valueChanged(double)"), self._updateBracketingInfo)
self._updateAdvanced()
def register():
""" Register plugins.
"""
PluginsManager().register(GphotoBracketShutter, GphotoBracketShutterController, capacity='shutter', name=NAME)
lordtangent wrote:I notice you are using the command line version of gphoto2. Is there a compile of gphoto2 out there anywhere for the Nokia N810? Is it even possible to use the N810 to control a camera via USB?
(I have a Netbook I could use for this also. But I want to try and get everything as compact as possible and use my N810 if I can.)
fma38 wrote:jeongyun, thanks for the doc.
About gphoto2 on N8x0, even if it has been ported, the big issue will be the USB port! I read it is possible to turn the N810 USB port to host-type (rather than slave), but it needs to be externally powered up... Not that simple!
lordtangent wrote:I did some Googling into it and it is totally possible. There are even off-the-shelf cable kits:
http://wiki.maemo.org/USB_host_mode
It seems that many devices DON'T require external power. However, if power is required, there are battery powered hubs!
(CyberPower CP-H420MP 4-Port USB Hub)
http://www.amazon.com/gp/product/B0002UQALQ
The N8X0 would be really handy as stand-alone controller for bracketing and time-lapse. Once I get the basic Bluetooth / Merlin stuff going I might delve into getting this going to gain more advanced bracketing right from the N810.
fma38 wrote:As I said, I plan to order a Nokia N800 USB 360 OTG Adapter. Is there anybody near Grenoble interested? Shipping cost from Canada is only $11, so ordering several items is only interesting if I can give them in hands...
fma38 wrote:Thanks for the links! I will order a usb adapter and make some tests.
gphoto2 port for maemo is here:
https://garage.maemo.org/frs/?group_id=837&release_id=2456
Also have a look at this thread:
http://talk.maemo.org/showthread.php?t=13868
If someone can make some tests...
lordtangent wrote:First of all, I want to report the OTG cable for the N810 from "business2828" works like a charm.
mediavets wrote:Does it automatically switch the N800 USB port into host mode?
Users browsing this forum: No registered users and 0 guests