diff --git a/data/ui/CameraMugshotDialog.ui b/data/ui/CameraMugshotDialog.ui new file mode 100644 index 0000000..2370e78 --- /dev/null +++ b/data/ui/CameraMugshotDialog.ui @@ -0,0 +1,102 @@ + + + + + + False + Capture - Mugshot + 300 + ../media/mugshot.svg + normal + + + + + + + True + False + vertical + 12 + + + True + False + True + True + center + + + gtk-cancel + True + False + True + True + + + False + True + 0 + + + + + gtk-media-record + True + False + True + True + + + False + True + 1 + True + + + + + gtk-apply + True + False + True + True + + + False + True + 2 + + + + + False + True + end + 0 + + + + + True + False + vertical + + + + + + True + True + 1 + + + + + + button1 + button2 + button3 + + + diff --git a/data/ui/MugshotWindow.ui b/data/ui/MugshotWindow.ui index 0a881ce..a34342a 100644 --- a/data/ui/MugshotWindow.ui +++ b/data/ui/MugshotWindow.ui @@ -39,10 +39,10 @@ Capture from camera... True - False False image2 False + diff --git a/data/ui/camera_mugshot_dialog.xml b/data/ui/camera_mugshot_dialog.xml new file mode 100644 index 0000000..12b16d2 --- /dev/null +++ b/data/ui/camera_mugshot_dialog.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/mugshot/CameraMugshotDialog.py b/mugshot/CameraMugshotDialog.py new file mode 100644 index 0000000..b17cbfc --- /dev/null +++ b/mugshot/CameraMugshotDialog.py @@ -0,0 +1,193 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +### BEGIN LICENSE +# Copyright (C) 2013 Sean Davis +# 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 . +### 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,)), + } + diff --git a/mugshot/MugshotWindow.py b/mugshot/MugshotWindow.py index acd17b6..3d14eeb 100644 --- a/mugshot/MugshotWindow.py +++ b/mugshot/MugshotWindow.py @@ -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: @@ -99,6 +100,8 @@ class MugshotWindow(Window): def finish_initializing(self, builder): # pylint: disable=E1002 """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') diff --git a/mugshot_lib/CameraDialog.py b/mugshot_lib/CameraDialog.py new file mode 100644 index 0000000..cf8aae3 --- /dev/null +++ b/mugshot_lib/CameraDialog.py @@ -0,0 +1,61 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +### BEGIN LICENSE +# Copyright (C) 2013 Sean Davis +# 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 . +### 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')) + diff --git a/mugshot_lib/Window.py b/mugshot_lib/Window.py index 1d25162..9a1222c 100644 --- a/mugshot_lib/Window.py +++ b/mugshot_lib/Window.py @@ -58,12 +58,37 @@ 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) 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."""