Initial camera, inspired by Rick Spencer's quidgets.
This commit is contained in:
parent
51c0c99d29
commit
4a932fa54a
|
@ -0,0 +1,102 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<!-- interface-requires gtk+ 3.0 -->
|
||||||
|
<!-- interface-requires camera_mugshot_dialog 1.0 -->
|
||||||
|
<object class="CameraMugshotDialog" id="camera_mugshot_dialog">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="title" translatable="yes">Capture - Mugshot</property>
|
||||||
|
<property name="default_height">300</property>
|
||||||
|
<property name="icon">../media/mugshot.svg</property>
|
||||||
|
<property name="type_hint">normal</property>
|
||||||
|
<signal name="destroy" handler="on_camera_mugshot_dialog_destroy" swapped="no"/>
|
||||||
|
<signal name="show" handler="on_camera_mugshot_dialog_show" swapped="no"/>
|
||||||
|
<signal name="delete-event" handler="on_camera_mugshot_dialog_delete_event" swapped="no"/>
|
||||||
|
<signal name="hide" handler="on_camera_mugshot_dialog_hide" swapped="no"/>
|
||||||
|
<child internal-child="vbox">
|
||||||
|
<object class="GtkBox" id="dialog-vbox1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<property name="spacing">12</property>
|
||||||
|
<child internal-child="action_area">
|
||||||
|
<object class="GtkButtonBox" id="dialog-action_area1">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="homogeneous">True</property>
|
||||||
|
<property name="layout_style">center</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button1">
|
||||||
|
<property name="label">gtk-cancel</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button2">
|
||||||
|
<property name="label">gtk-media-record</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
<property name="non_homogeneous">True</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButton" id="button3">
|
||||||
|
<property name="label">gtk-apply</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="receives_default">True</property>
|
||||||
|
<property name="use_stock">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="pack_type">end</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="camera_box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="orientation">vertical</property>
|
||||||
|
<child>
|
||||||
|
<placeholder/>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<action-widgets>
|
||||||
|
<action-widget response="-6">button1</action-widget>
|
||||||
|
<action-widget response="0">button2</action-widget>
|
||||||
|
<action-widget response="-10">button3</action-widget>
|
||||||
|
</action-widgets>
|
||||||
|
</object>
|
||||||
|
</interface>
|
|
@ -39,10 +39,10 @@
|
||||||
<object class="GtkImageMenuItem" id="image_from_camera">
|
<object class="GtkImageMenuItem" id="image_from_camera">
|
||||||
<property name="label" translatable="yes">Capture from camera...</property>
|
<property name="label" translatable="yes">Capture from camera...</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="sensitive">False</property>
|
|
||||||
<property name="can_focus">False</property>
|
<property name="can_focus">False</property>
|
||||||
<property name="image">image2</property>
|
<property name="image">image2</property>
|
||||||
<property name="use_stock">False</property>
|
<property name="use_stock">False</property>
|
||||||
|
<signal name="activate" handler="on_menu_camera_activate" swapped="no"/>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<glade-catalog name="camera_mugshot_dialog" domain="glade-3"
|
||||||
|
depends="gtk+" version="1.0">
|
||||||
|
<glade-widget-classes>
|
||||||
|
<glade-widget-class title="Mugshot Preferences Dialog" name="CameraMugshotDialog"
|
||||||
|
generic-name="CameraMugshotDialog" parent="GtkDialog"
|
||||||
|
icon-name="widget-gtk-dialog"/>
|
||||||
|
</glade-widget-classes>
|
||||||
|
|
||||||
|
</glade-catalog>
|
|
@ -0,0 +1,193 @@
|
||||||
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
|
### BEGIN LICENSE
|
||||||
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
|
# by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
### END LICENSE
|
||||||
|
|
||||||
|
from locale import gettext as _
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('mugshot')
|
||||||
|
|
||||||
|
from gi.repository import Gtk, GdkX11, GObject, Gst, GstVideo
|
||||||
|
|
||||||
|
from mugshot_lib.CameraDialog import CameraDialog
|
||||||
|
|
||||||
|
class CameraMugshotDialog(CameraDialog):
|
||||||
|
__gtype_name__ = "CameraMugshotDialog"
|
||||||
|
|
||||||
|
def finish_initializing(self, builder): # pylint: disable=E1002
|
||||||
|
"""Set up the preferences dialog"""
|
||||||
|
super(CameraMugshotDialog, self).finish_initializing(builder)
|
||||||
|
|
||||||
|
Gst.init(None)
|
||||||
|
|
||||||
|
# Code for other initialization actions should be added here.
|
||||||
|
vbox = builder.get_object('camera_box')
|
||||||
|
self.video_window = Gtk.DrawingArea()
|
||||||
|
self.video_window.connect("realize",self.__on_video_window_realized)
|
||||||
|
vbox.pack_start(self.video_window, True, True, 0)
|
||||||
|
self.video_window.show()
|
||||||
|
|
||||||
|
self.camerabin = Gst.ElementFactory.make("camerabin", "camera-source")
|
||||||
|
bus = self.camerabin.get_bus()
|
||||||
|
bus.add_signal_watch()
|
||||||
|
bus.enable_sync_message_emission()
|
||||||
|
bus.connect("message", self._on_message)
|
||||||
|
bus.connect("sync-message::element", self._on_sync_message)
|
||||||
|
self.realized = False
|
||||||
|
self.show_all()
|
||||||
|
##self.play()
|
||||||
|
|
||||||
|
def play(self):
|
||||||
|
"""play - Start the camera streaming and display the output. It is
|
||||||
|
necessary to start the camera playing before using most other functions.
|
||||||
|
|
||||||
|
This function has no arguments
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not self.realized:
|
||||||
|
self._set_video_window_id()
|
||||||
|
if not self.realized:
|
||||||
|
print _("Cannot display web cam output. Ignoring play command")
|
||||||
|
else:
|
||||||
|
self.camerabin.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
"""pause - Pause the camera output. It will cause the image to
|
||||||
|
"freeze". Use play() to start the camera playng again. Note that
|
||||||
|
calling pause before play may cause errors on certain camera.
|
||||||
|
|
||||||
|
This function has no arguments
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.camerabin.set_state(Gst.State.PAUSED)
|
||||||
|
|
||||||
|
def take_picture(self, filename):
|
||||||
|
"""take_picture - grab a frame from the web cam and save it to
|
||||||
|
~/Pictures/datestamp.png, where datestamp is the current datestamp.
|
||||||
|
It's possible to add a prefix to the datestamp by setting the
|
||||||
|
filename_prefix property.
|
||||||
|
|
||||||
|
If play is not called before take_picture,
|
||||||
|
an error may occur. If take_picture is called immediately after play,
|
||||||
|
the camera may not be fully initialized, and an error may occur.
|
||||||
|
|
||||||
|
Connect to the signal "image-captured" to be alerted when the picture
|
||||||
|
is saved.
|
||||||
|
|
||||||
|
This function has no arguments
|
||||||
|
|
||||||
|
returns - a string of the filename used to save the image
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.camerabin.set_property("location", filename)
|
||||||
|
self.camerabin.emit("start-capture")
|
||||||
|
return filename
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""stop - Stop the camera streaming and display the output.
|
||||||
|
|
||||||
|
This function has no arguments
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.camerabin.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
|
def _on_message(self, bus, message):
|
||||||
|
""" _on_message - internal signal handler for bus messages.
|
||||||
|
May be useful to extend in a base class to handle messages
|
||||||
|
produced from custom behaviors.
|
||||||
|
|
||||||
|
|
||||||
|
arguments -
|
||||||
|
bus: the bus from which the message was sent, typically self.bux
|
||||||
|
message: the message sent
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if message is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
t = message.type
|
||||||
|
#if t == Gst.MessageType.ASYNC_DONE:
|
||||||
|
# print 'finally'
|
||||||
|
if t == Gst.MessageType.ELEMENT:
|
||||||
|
if message.get_structure().get_name() == "image-captured":
|
||||||
|
#work around to keep the camera working after lots
|
||||||
|
#of pictures are taken
|
||||||
|
self.camerabin.set_state(Gst.Sate.NULL)
|
||||||
|
self.camerabin.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
|
self.emit("image-captured", self.filename)
|
||||||
|
|
||||||
|
if t == Gst.MessageType.EOS:
|
||||||
|
self.camerabin.set_state(Gst.State.NULL)
|
||||||
|
elif t == Gst.MessageType.ERROR:
|
||||||
|
err, debug = message.parse_error()
|
||||||
|
print "Error: %s" % err, debug
|
||||||
|
|
||||||
|
def _on_sync_message(self, bus, message):
|
||||||
|
""" _on_sync_message - internal signal handler for bus messages.
|
||||||
|
May be useful to extend in a base class to handle messages
|
||||||
|
produced from custom behaviors.
|
||||||
|
|
||||||
|
|
||||||
|
arguments -
|
||||||
|
bus: the bus from which the message was sent, typically self.bux
|
||||||
|
message: the message sent
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
if message.get_structure() is None:
|
||||||
|
return
|
||||||
|
message_name = message.get_structure().get_name()
|
||||||
|
if message_name == "prepare-window-handle":
|
||||||
|
imagesink = message.src
|
||||||
|
imagesink.set_property("force-aspect-ratio", True)
|
||||||
|
imagesink.set_window_handle(self.video_window.get_window().get_xid())
|
||||||
|
|
||||||
|
def __on_video_window_realized(self, widget, data=None):
|
||||||
|
"""__on_video_window_realized - internal signal handler, used
|
||||||
|
to set up the xid for the drawing area in thread safe manner.
|
||||||
|
Do not call directly.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self._set_video_window_id()
|
||||||
|
|
||||||
|
def _set_video_window_id(self):
|
||||||
|
if not self.realized and self.video_window.get_window() is not None:
|
||||||
|
x = self.video_window.get_window().get_xid()
|
||||||
|
self.realized = True
|
||||||
|
|
||||||
|
def on_camera_mugshot_dialog_destroy(self, widget, data=None):
|
||||||
|
#clean up the camera before exiting
|
||||||
|
self.camerabin.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
|
def on_camera_mugshot_dialog_hide(self, widget, data=None):
|
||||||
|
self.pause()
|
||||||
|
|
||||||
|
def on_camera_mugshot_dialog_show(self, widget, data=None):
|
||||||
|
self.show_all()
|
||||||
|
self.play()
|
||||||
|
|
||||||
|
def on_camera_mugshot_dialog_delete_event(self, widget, data=None):
|
||||||
|
self.hide()
|
||||||
|
return True
|
||||||
|
|
||||||
|
__gsignals__ = {'image-captured' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
|
||||||
|
(GObject.TYPE_PYOBJECT,)),
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ import logging
|
||||||
logger = logging.getLogger('mugshot')
|
logger = logging.getLogger('mugshot')
|
||||||
|
|
||||||
from mugshot_lib import Window
|
from mugshot_lib import Window
|
||||||
|
from mugshot.CameraMugshotDialog import CameraMugshotDialog
|
||||||
|
|
||||||
username = os.getenv('USER')
|
username = os.getenv('USER')
|
||||||
if not username:
|
if not username:
|
||||||
|
@ -100,6 +101,8 @@ class MugshotWindow(Window):
|
||||||
"""Set up the main window"""
|
"""Set up the main window"""
|
||||||
super(MugshotWindow, self).finish_initializing(builder)
|
super(MugshotWindow, self).finish_initializing(builder)
|
||||||
|
|
||||||
|
self.CameraDialog = CameraMugshotDialog
|
||||||
|
|
||||||
# User Image widgets
|
# User Image widgets
|
||||||
self.image_button = builder.get_object('image_button')
|
self.image_button = builder.get_object('image_button')
|
||||||
self.user_image = builder.get_object('user_image')
|
self.user_image = builder.get_object('user_image')
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
|
### BEGIN LICENSE
|
||||||
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
|
# This program is free software: you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
|
# by the Free Software Foundation.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
### END LICENSE
|
||||||
|
|
||||||
|
### DO NOT EDIT THIS FILE ###
|
||||||
|
|
||||||
|
from gi.repository import Gtk # pylint: disable=E0611
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('mugshot_lib')
|
||||||
|
|
||||||
|
from . helpers import get_builder, show_uri, get_help_uri
|
||||||
|
|
||||||
|
class CameraDialog(Gtk.Dialog):
|
||||||
|
__gtype_name__ = "CameraDialog"
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
"""Special static method that's automatically called by Python when
|
||||||
|
constructing a new instance of this class.
|
||||||
|
|
||||||
|
Returns a fully instantiated PreferencesDialog object.
|
||||||
|
"""
|
||||||
|
builder = get_builder('CameraMugshotDialog')
|
||||||
|
new_object = builder.get_object("camera_mugshot_dialog")
|
||||||
|
new_object.finish_initializing(builder)
|
||||||
|
return new_object
|
||||||
|
|
||||||
|
def finish_initializing(self, builder):
|
||||||
|
"""Called while initializing this instance in __new__
|
||||||
|
|
||||||
|
finish_initalizing should be called after parsing the ui definition
|
||||||
|
and creating a PreferencesDialog object with it in order to
|
||||||
|
finish initializing the start of the new PerferencesMugshotDialog
|
||||||
|
instance.
|
||||||
|
|
||||||
|
Put your initialization code in here and leave __init__ undefined.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Get a reference to the builder and set up the signals.
|
||||||
|
self.builder = builder
|
||||||
|
self.ui = builder.get_ui(self, True)
|
||||||
|
|
||||||
|
# code for other initialization actions should be added here
|
||||||
|
|
||||||
|
def on_btn_close_clicked(self, widget, data=None):
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def on_btn_help_clicked(self, widget, data=None):
|
||||||
|
show_uri(self, "ghelp:%s" % get_help_uri('preferences'))
|
||||||
|
|
|
@ -58,6 +58,8 @@ class Window(Gtk.Window):
|
||||||
# Get a reference to the builder and set up the signals.
|
# Get a reference to the builder and set up the signals.
|
||||||
self.builder = builder
|
self.builder = builder
|
||||||
self.ui = builder.get_ui(self, True)
|
self.ui = builder.get_ui(self, True)
|
||||||
|
self.CameraDialog = None # class
|
||||||
|
self.camera_dialog = None # instance
|
||||||
|
|
||||||
self.settings = Gio.Settings("apps.mugshot")
|
self.settings = Gio.Settings("apps.mugshot")
|
||||||
self.settings.connect('changed', self.on_preferences_changed)
|
self.settings.connect('changed', self.on_preferences_changed)
|
||||||
|
@ -65,6 +67,29 @@ class Window(Gtk.Window):
|
||||||
def on_help_activate(self, widget, data=None):
|
def on_help_activate(self, widget, data=None):
|
||||||
show_uri(self, "ghelp:%s" % get_help_uri())
|
show_uri(self, "ghelp:%s" % get_help_uri())
|
||||||
|
|
||||||
|
def on_menu_camera_activate(self, widget, data=None):
|
||||||
|
"""Display the camera window for mugshot."""
|
||||||
|
if self.camera_dialog is not None:
|
||||||
|
logger.debug('show existing camera_dialog')
|
||||||
|
self.camera_dialog.show()
|
||||||
|
elif self.CameraDialog is not None:
|
||||||
|
logger.debug('create new camera_dialog')
|
||||||
|
self.camera_dialog = self.CameraDialog() # pylint: disable=E1102
|
||||||
|
#self.camera_dialog.connect('destroy', self.on_camera_dialog_destroyed)
|
||||||
|
self.camera_dialog.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#def on_camera_dialog_destroyed(self, widget, data=None):
|
||||||
|
# '''only affects gui
|
||||||
|
#
|
||||||
|
# logically there is no difference between the user closing,
|
||||||
|
# minimising or ignoring the camera dialog'''
|
||||||
|
# logger.debug('on_camera_dialog_destroyed')
|
||||||
|
# # to determine whether to create or present camera_dialog
|
||||||
|
# self.camera_dialog.hide()
|
||||||
|
# #self.camera_dialog = None
|
||||||
|
|
||||||
def on_destroy(self, widget, data=None):
|
def on_destroy(self, widget, data=None):
|
||||||
"""Called when the MugshotWindow is closed."""
|
"""Called when the MugshotWindow is closed."""
|
||||||
# Clean up code for saving application state should be added here.
|
# Clean up code for saving application state should be added here.
|
||||||
|
|
Loading…
Reference in New Issue