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">
|
||||
<property name="label" translatable="yes">Capture from camera...</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="image">image2</property>
|
||||
<property name="use_stock">False</property>
|
||||
<signal name="activate" handler="on_menu_camera_activate" swapped="no"/>
|
||||
</object>
|
||||
</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')
|
||||
|
||||
from mugshot_lib import Window
|
||||
from mugshot.CameraMugshotDialog import CameraMugshotDialog
|
||||
|
||||
username = os.getenv('USER')
|
||||
if not username:
|
||||
|
@ -100,6 +101,8 @@ class MugshotWindow(Window):
|
|||
"""Set up the main window"""
|
||||
super(MugshotWindow, self).finish_initializing(builder)
|
||||
|
||||
self.CameraDialog = CameraMugshotDialog
|
||||
|
||||
# User Image widgets
|
||||
self.image_button = builder.get_object('image_button')
|
||||
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.
|
||||
self.builder = builder
|
||||
self.ui = builder.get_ui(self, True)
|
||||
self.CameraDialog = None # class
|
||||
self.camera_dialog = None # instance
|
||||
|
||||
self.settings = Gio.Settings("apps.mugshot")
|
||||
self.settings.connect('changed', self.on_preferences_changed)
|
||||
|
@ -65,6 +67,29 @@ class Window(Gtk.Window):
|
|||
def on_help_activate(self, widget, data=None):
|
||||
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):
|
||||
"""Called when the MugshotWindow is closed."""
|
||||
# Clean up code for saving application state should be added here.
|
||||
|
|
Loading…
Reference in New Issue