Python cleanup
This commit is contained in:
parent
653fc5a108
commit
527fa8cf4c
|
@ -3,16 +3,16 @@
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# Copyright (C) 2010 Rick Spencer <rick.spencer@canonical.com>
|
# Copyright (C) 2010 Rick Spencer <rick.spencer@canonical.com>
|
||||||
# Portions of this code are inspired by web_cam_box by Rick Spencer.
|
# Portions of this code are inspired by web_cam_box by Rick Spencer.
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
|
@ -21,65 +21,69 @@ from locale import gettext as _
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('mugshot')
|
logger = logging.getLogger('mugshot')
|
||||||
|
|
||||||
from gi.repository import Gtk, GdkX11, GObject, Gst, GstVideo, GdkPixbuf
|
from gi.repository import Gtk, GObject, Gst, GdkPixbuf
|
||||||
import cairo
|
import cairo
|
||||||
|
|
||||||
import tempfile, os
|
import tempfile
|
||||||
|
import os
|
||||||
|
|
||||||
from mugshot_lib.CameraDialog import CameraDialog
|
from mugshot_lib.CameraDialog import CameraDialog
|
||||||
|
|
||||||
|
|
||||||
def draw_message(widget, message, ctx):
|
def draw_message(widget, message, ctx):
|
||||||
"""Draw a message (including newlines) vertically centered on a cairo context."""
|
"""Draw a message (including newlines) vertically centered on a cairo
|
||||||
|
context."""
|
||||||
split_msg = message.split('\n')
|
split_msg = message.split('\n')
|
||||||
|
|
||||||
# Get the height and width of the drawing area.
|
# Get the height and width of the drawing area.
|
||||||
alloc = widget.get_allocation()
|
alloc = widget.get_allocation()
|
||||||
height = alloc.height
|
height = alloc.height
|
||||||
width = alloc.width
|
|
||||||
|
|
||||||
# Make the background black.
|
# Make the background black.
|
||||||
ctx.set_source_rgb(0,0,0)
|
ctx.set_source_rgb(0, 0, 0)
|
||||||
ctx.paint()
|
ctx.paint()
|
||||||
|
|
||||||
# Set the font details.
|
# Set the font details.
|
||||||
font_size = 20
|
font_size = 20
|
||||||
font_color = (255,255,255)
|
font_color = (255, 255, 255)
|
||||||
font_name = "Sans"
|
font_name = "Sans"
|
||||||
row_spacing = 6
|
row_spacing = 6
|
||||||
left_spacing = 10
|
left_spacing = 10
|
||||||
|
|
||||||
# Get start position
|
# Get start position
|
||||||
message_height = (len(split_msg)*font_size)+len(split_msg)-1-14
|
message_height = (len(split_msg) * font_size) + len(split_msg) - 15
|
||||||
current_pos = (height-message_height)/2
|
current_pos = (height - message_height) / 2
|
||||||
|
|
||||||
# Draw the message to the drawing area.
|
# Draw the message to the drawing area.
|
||||||
ctx.set_source_rgb(*font_color)
|
ctx.set_source_rgb(*font_color)
|
||||||
ctx.select_font_face(font_name, cairo.FONT_SLANT_NORMAL,
|
ctx.select_font_face(font_name, cairo.FONT_SLANT_NORMAL,
|
||||||
cairo.FONT_WEIGHT_NORMAL)
|
cairo.FONT_WEIGHT_NORMAL)
|
||||||
ctx.set_font_size(font_size)
|
ctx.set_font_size(font_size)
|
||||||
|
|
||||||
for line in split_msg:
|
for line in split_msg:
|
||||||
ctx.move_to(left_spacing,current_pos)
|
ctx.move_to(left_spacing, current_pos)
|
||||||
ctx.show_text(line)
|
ctx.show_text(line)
|
||||||
current_pos = current_pos + font_size + row_spacing
|
current_pos = current_pos + font_size + row_spacing
|
||||||
|
|
||||||
|
|
||||||
class CameraMugshotDialog(CameraDialog):
|
class CameraMugshotDialog(CameraDialog):
|
||||||
|
"""Camera Capturing Dialog"""
|
||||||
__gtype_name__ = "CameraMugshotDialog"
|
__gtype_name__ = "CameraMugshotDialog"
|
||||||
|
|
||||||
def finish_initializing(self, builder): # pylint: disable=E1002
|
def finish_initializing(self, builder): # pylint: disable=E1002
|
||||||
"""Set up the camera dialog"""
|
"""Set up the camera dialog"""
|
||||||
super(CameraMugshotDialog, self).finish_initializing(builder)
|
super(CameraMugshotDialog, self).finish_initializing(builder)
|
||||||
|
|
||||||
# Initialize Gst or nothing will work.
|
# Initialize Gst or nothing will work.
|
||||||
Gst.init(None)
|
Gst.init(None)
|
||||||
|
|
||||||
# Pack the video widget into the dialog.
|
# Pack the video widget into the dialog.
|
||||||
vbox = builder.get_object('camera_box')
|
vbox = builder.get_object('camera_box')
|
||||||
self.video_window = Gtk.DrawingArea()
|
self.video_window = Gtk.DrawingArea()
|
||||||
self.video_window.connect("realize",self.__on_video_window_realized)
|
self.video_window.connect("realize", self.__on_video_window_realized)
|
||||||
vbox.pack_start(self.video_window, True, True, 0)
|
vbox.pack_start(self.video_window, True, True, 0)
|
||||||
self.video_window.show()
|
self.video_window.show()
|
||||||
|
|
||||||
# Prepare the camerabin element.
|
# Prepare the camerabin element.
|
||||||
self.camerabin = Gst.ElementFactory.make("camerabin", "camera-source")
|
self.camerabin = Gst.ElementFactory.make("camerabin", "camera-source")
|
||||||
if self.camerabin:
|
if self.camerabin:
|
||||||
|
@ -94,55 +98,62 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
else:
|
else:
|
||||||
devices = []
|
devices = []
|
||||||
for device in os.listdir('/dev/'):
|
for device in os.listdir('/dev/'):
|
||||||
if device.startswith('video'): devices.append(device)
|
if device.startswith('video'):
|
||||||
logger.error(_('Camera failed to load. Devices: %s') % '; '.join(devices))
|
devices.append(device)
|
||||||
self.draw_handler = self.video_window.connect('draw', self.on_failed_draw)
|
logger.error(_('Camera failed to load. Devices: %s') %
|
||||||
|
'; '.join(devices))
|
||||||
|
self.draw_handler = self.video_window.connect('draw',
|
||||||
|
self.on_failed_draw)
|
||||||
self.realized = True
|
self.realized = True
|
||||||
|
|
||||||
# Essential widgets
|
# Essential widgets
|
||||||
self.record_button = builder.get_object('camera_record')
|
self.record_button = builder.get_object('camera_record')
|
||||||
self.apply_button = builder.get_object('camera_apply')
|
self.apply_button = builder.get_object('camera_apply')
|
||||||
|
|
||||||
# Store the temporary filename to be used.
|
# Store the temporary filename to be used.
|
||||||
self.filename = None
|
self.filename = None
|
||||||
|
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def on_failed_draw(self, widget, ctx):
|
def on_failed_draw(self, widget, ctx):
|
||||||
"""Display a message that the camera failed to load."""
|
"""Display a message that the camera failed to load."""
|
||||||
# Translators: Please include newlines, as required to fit the message.
|
# Translators: Please include newlines, as required to fit the message.
|
||||||
message = _("Sorry, but your camera\nfailed to initialize.")
|
message = _("Sorry, but your camera\nfailed to initialize.")
|
||||||
draw_message(widget, message, ctx)
|
draw_message(widget, message, ctx)
|
||||||
|
|
||||||
def on_draw(self, widget, ctx):
|
def on_draw(self, widget, ctx):
|
||||||
"""Display a message that the camera is initializing on first draw.
|
"""Display a message that the camera is initializing on first draw.
|
||||||
Afterwards, blank the drawing area to clear the message."""
|
Afterwards, blank the drawing area to clear the message."""
|
||||||
# Translators: Please include newlines, as required to fit the message.
|
# Translators: Please include newlines, as required to fit the message.
|
||||||
message = _("Please wait while your\ncamera is initialized.")
|
message = _("Please wait while your\ncamera is initialized.")
|
||||||
draw_message(widget, message, ctx)
|
draw_message(widget, message, ctx)
|
||||||
|
|
||||||
# Redefine on_draw to blank the drawing area next time.
|
# Redefine on_draw to blank the drawing area next time.
|
||||||
def on_draw(self, widget, ctx):
|
def on_draw(self, widget, ctx):
|
||||||
ctx.set_source_rgb(0,0,0)
|
"""Redefinition of on_draw to blank the drawing area next time."""
|
||||||
|
ctx.set_source_rgb(0, 0, 0)
|
||||||
ctx.paint()
|
ctx.paint()
|
||||||
|
|
||||||
# Redefine on_draw once more to do nothing else.
|
# Redefine on_draw once more to do nothing else.
|
||||||
def on_draw(self, widget, ctx):
|
def on_draw(self, widget, ctx):
|
||||||
|
"""Redefinition of on_draw no longer do anything."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def play(self):
|
def play(self):
|
||||||
"""Start the camera streaming and display the output. It is necessary
|
"""Start the camera streaming and display the output. It is necessary
|
||||||
to start the camera playing before using most other functions."""
|
to start the camera playing before using most other functions."""
|
||||||
if not self.realized:
|
if not self.realized:
|
||||||
self._set_video_window_id()
|
self._set_video_window_id()
|
||||||
if not self.realized:
|
if not self.realized:
|
||||||
logger.error(_("Cannot display camera output. Ignoring play command"))
|
logger.error(_("Cannot display camera output."
|
||||||
|
"Ignoring play command"))
|
||||||
else:
|
else:
|
||||||
if self.camerabin:
|
if self.camerabin:
|
||||||
self.camerabin.set_state(Gst.State.PLAYING)
|
self.camerabin.set_state(Gst.State.PLAYING)
|
||||||
|
|
||||||
def pause(self):
|
def pause(self):
|
||||||
"""Pause the camera output. It will cause the image to "freeze".
|
"""Pause the camera output. It will cause the image to "freeze".
|
||||||
Use play() to start the camera playing again. Note that calling pause
|
Use play() to start the camera playing again. Note that calling pause
|
||||||
before play may cause errors on certain camera."""
|
before play may cause errors on certain camera."""
|
||||||
if self.camerabin:
|
if self.camerabin:
|
||||||
self.camerabin.set_state(Gst.State.PAUSED)
|
self.camerabin.set_state(Gst.State.PAUSED)
|
||||||
|
@ -150,11 +161,11 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
def take_picture(self, filename):
|
def take_picture(self, filename):
|
||||||
"""take_picture - grab a frame from the webcam and save it to
|
"""take_picture - grab a frame from the webcam and save it to
|
||||||
'filename.
|
'filename.
|
||||||
|
|
||||||
If play is not called before take_picture,
|
If play is not called before take_picture,
|
||||||
an error may occur. If take_picture is called immediately after play,
|
an error may occur. If take_picture is called immediately after play,
|
||||||
the camera may not be fully initialized, and an error may occur.
|
the camera may not be fully initialized, and an error may occur.
|
||||||
|
|
||||||
Connect to the signal "image-captured" to be alerted when the picture
|
Connect to the signal "image-captured" to be alerted when the picture
|
||||||
is saved."""
|
is saved."""
|
||||||
self.camerabin.set_property("location", filename)
|
self.camerabin.set_property("location", filename)
|
||||||
|
@ -178,18 +189,18 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
|
|
||||||
# Get the message type.
|
# Get the message type.
|
||||||
t = message.type
|
t = message.type
|
||||||
|
|
||||||
# Initial load, wait until camera is ready before enabling capture.
|
# Initial load, wait until camera is ready before enabling capture.
|
||||||
if t == Gst.MessageType.ASYNC_DONE:
|
if t == Gst.MessageType.ASYNC_DONE:
|
||||||
self.record_button.set_sensitive(True)
|
self.record_button.set_sensitive(True)
|
||||||
|
|
||||||
if t == Gst.MessageType.ELEMENT:
|
if t == Gst.MessageType.ELEMENT:
|
||||||
# Keep the camera working after several pictures are taken.
|
# Keep the camera working after several pictures are taken.
|
||||||
if message.get_structure().get_name() == "image-captured":
|
if message.get_structure().get_name() == "image-captured":
|
||||||
self.camerabin.set_state(Gst.Sate.NULL)
|
self.camerabin.set_state(Gst.Sate.NULL)
|
||||||
self.camerabin.set_state(Gst.State.PLAYING)
|
self.camerabin.set_state(Gst.State.PLAYING)
|
||||||
self.emit("image-captured", self.filename)
|
self.emit("image-captured", self.filename)
|
||||||
|
|
||||||
# Enable interface elements once the images are finished saving.
|
# Enable interface elements once the images are finished saving.
|
||||||
elif message.get_structure().get_name() == "image-done":
|
elif message.get_structure().get_name() == "image-done":
|
||||||
self.apply_button.set_sensitive(True)
|
self.apply_button.set_sensitive(True)
|
||||||
|
@ -199,7 +210,7 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
# Stop the stream if the EOS (end of stream) message is received.
|
# Stop the stream if the EOS (end of stream) message is received.
|
||||||
if t == Gst.MessageType.EOS:
|
if t == Gst.MessageType.EOS:
|
||||||
self.camerabin.set_state(Gst.State.NULL)
|
self.camerabin.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
# Capture and report any error received.
|
# Capture and report any error received.
|
||||||
elif t == Gst.MessageType.ERROR:
|
elif t == Gst.MessageType.ERROR:
|
||||||
err, debug = message.parse_error()
|
err, debug = message.parse_error()
|
||||||
|
@ -223,48 +234,49 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
if message_name == "prepare-window-handle":
|
if message_name == "prepare-window-handle":
|
||||||
imagesink = message.src
|
imagesink = message.src
|
||||||
imagesink.set_property("force-aspect-ratio", True)
|
imagesink.set_property("force-aspect-ratio", True)
|
||||||
imagesink.set_window_handle(self.video_window.get_window().get_xid())
|
imagesink.set_window_handle(
|
||||||
|
self.video_window.get_window().get_xid())
|
||||||
|
|
||||||
def __on_video_window_realized(self, widget, data=None):
|
def __on_video_window_realized(self, widget, data=None):
|
||||||
"""Internal signal handler, used to set up the xid for the drawing area
|
"""Internal signal handler, used to set up the xid for the drawing area
|
||||||
in a thread safe manner. Do not call directly."""
|
in a thread safe manner. Do not call directly."""
|
||||||
self._set_video_window_id()
|
self._set_video_window_id()
|
||||||
|
|
||||||
def _set_video_window_id(self):
|
def _set_video_window_id(self):
|
||||||
"""Set the window ID only if not previously configured."""
|
"""Set the window ID only if not previously configured."""
|
||||||
if not self.realized and self.video_window.get_window() is not None:
|
if not self.realized and self.video_window.get_window() is not None:
|
||||||
x = self.video_window.get_window().get_xid()
|
self.video_window.get_window().get_xid()
|
||||||
self.realized = True
|
self.realized = True
|
||||||
|
|
||||||
def on_camera_record_clicked(self, widget):
|
def on_camera_record_clicked(self, widget):
|
||||||
"""When the camera record/retry button is clicked:
|
"""When the camera record/retry button is clicked:
|
||||||
Record: Pause the video, start the capture, enable apply and retry.
|
Record: Pause the video, start the capture, enable apply and retry.
|
||||||
Retry: Restart the video stream."""
|
Retry: Restart the video stream."""
|
||||||
# Remove any previous temporary file.
|
# Remove any previous temporary file.
|
||||||
if self.filename and os.path.isfile(self.filename):
|
if self.filename and os.path.isfile(self.filename):
|
||||||
os.remove(self.filename)
|
os.remove(self.filename)
|
||||||
|
|
||||||
# Retry action.
|
# Retry action.
|
||||||
if self.apply_button.get_sensitive():
|
if self.apply_button.get_sensitive():
|
||||||
self.record_button.set_label(Gtk.STOCK_MEDIA_RECORD)
|
self.record_button.set_label(Gtk.STOCK_MEDIA_RECORD)
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
self.play()
|
self.play()
|
||||||
|
|
||||||
# Record (Capture) action.
|
# Record (Capture) action.
|
||||||
else:
|
else:
|
||||||
# Create a new temporary file.
|
# Create a new temporary file.
|
||||||
tmpfile = tempfile.NamedTemporaryFile(delete=False)
|
tmpfile = tempfile.NamedTemporaryFile(delete=False)
|
||||||
tmpfile.close()
|
tmpfile.close()
|
||||||
self.filename = tmpfile.name
|
self.filename = tmpfile.name
|
||||||
|
|
||||||
# Capture the current image.
|
# Capture the current image.
|
||||||
self.take_picture(self.filename)
|
self.take_picture(self.filename)
|
||||||
|
|
||||||
# Set the record button to retry, and disable it until the capture
|
# Set the record button to retry, and disable it until the capture
|
||||||
# finishes.
|
# finishes.
|
||||||
self.record_button.set_label(_("Retry"))
|
self.record_button.set_label(_("Retry"))
|
||||||
self.record_button.set_sensitive(False)
|
self.record_button.set_sensitive(False)
|
||||||
|
|
||||||
def on_camera_apply_clicked(self, widget):
|
def on_camera_apply_clicked(self, widget):
|
||||||
"""When the camera Apply button is clicked, crop the current photo and
|
"""When the camera Apply button is clicked, crop the current photo and
|
||||||
emit a signal to let the main application know there is a new file
|
emit a signal to let the main application know there is a new file
|
||||||
|
@ -272,7 +284,7 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
self.center_crop(self.filename)
|
self.center_crop(self.filename)
|
||||||
self.emit("apply", self.filename)
|
self.emit("apply", self.filename)
|
||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
def on_camera_cancel_clicked(self, widget):
|
def on_camera_cancel_clicked(self, widget):
|
||||||
"""When the Cancel button is clicked, just hide the dialog."""
|
"""When the Cancel button is clicked, just hide the dialog."""
|
||||||
self.hide()
|
self.hide()
|
||||||
|
@ -285,54 +297,56 @@ class CameraMugshotDialog(CameraDialog):
|
||||||
os.remove(self.filename)
|
os.remove(self.filename)
|
||||||
# Clean up the camera before exiting
|
# Clean up the camera before exiting
|
||||||
self.camerabin.set_state(Gst.State.NULL)
|
self.camerabin.set_state(Gst.State.NULL)
|
||||||
|
|
||||||
def on_camera_mugshot_dialog_hide(self, widget, data=None):
|
def on_camera_mugshot_dialog_hide(self, widget, data=None):
|
||||||
"""When the dialog is hidden, pause the camera recording."""
|
"""When the dialog is hidden, pause the camera recording."""
|
||||||
self.pause()
|
self.pause()
|
||||||
|
|
||||||
def on_camera_mugshot_dialog_show(self, widget, data=None):
|
def on_camera_mugshot_dialog_show(self, widget, data=None):
|
||||||
"""When the dialog is shown, set the record button to record, disable
|
"""When the dialog is shown, set the record button to record, disable
|
||||||
the apply button, and start the camera."""
|
the apply button, and start the camera."""
|
||||||
self.record_button.set_label(Gtk.STOCK_MEDIA_RECORD)
|
self.record_button.set_label(Gtk.STOCK_MEDIA_RECORD)
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
self.show_all()
|
self.show_all()
|
||||||
self.play()
|
self.play()
|
||||||
|
|
||||||
def center_crop(self, filename):
|
def center_crop(self, filename):
|
||||||
"""Crop the specified file to square dimensions."""
|
"""Crop the specified file to square dimensions."""
|
||||||
# Load the image into a Pixbuf.
|
# Load the image into a Pixbuf.
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
||||||
|
|
||||||
# Get the image dimensions.
|
# Get the image dimensions.
|
||||||
height = pixbuf.get_height()
|
height = pixbuf.get_height()
|
||||||
width = pixbuf.get_width()
|
width = pixbuf.get_width()
|
||||||
start_x = 0
|
start_x = 0
|
||||||
start_y = 0
|
start_y = 0
|
||||||
|
|
||||||
# Calculate a balanced center.
|
# Calculate a balanced center.
|
||||||
if width > height:
|
if width > height:
|
||||||
start_x = (width-height)/2
|
start_x = (width - height) / 2
|
||||||
width = height
|
width = height
|
||||||
else:
|
else:
|
||||||
start_y = (height-width)/2
|
start_y = (height - width) / 2
|
||||||
height = width
|
height = width
|
||||||
|
|
||||||
# Create a new cropped pixbuf.
|
# Create a new cropped pixbuf.
|
||||||
new_pixbuf = pixbuf.new_subpixbuf(start_x, start_y, width, height)
|
new_pixbuf = pixbuf.new_subpixbuf(start_x, start_y, width, height)
|
||||||
|
|
||||||
# Overwrite the temporary file with our new cropped image.
|
# Overwrite the temporary file with our new cropped image.
|
||||||
new_pixbuf.savev(filename, "png", [], [])
|
new_pixbuf.savev(filename, "png", [], [])
|
||||||
|
|
||||||
def on_camera_mugshot_dialog_delete_event(self, widget, data=None):
|
def on_camera_mugshot_dialog_delete_event(self, widget, data=None):
|
||||||
"""Override the dialog delete event to just hide the window."""
|
"""Override the dialog delete event to just hide the window."""
|
||||||
self.hide()
|
self.hide()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Signals used by CameraMugshotDialog:
|
# Signals used by CameraMugshotDialog:
|
||||||
# image-captured: emitted when the camera is done capturing an image.
|
# image-captured: emitted when the camera is done capturing an image.
|
||||||
# apply: emitted when the apply button has been pressed and there is a new file saved for use.
|
# apply: emitted when the apply button has been pressed and there is a
|
||||||
__gsignals__ = {'image-captured' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
|
# new file saved for use.
|
||||||
(GObject.TYPE_PYOBJECT,)),
|
__gsignals__ = {'image-captured': (GObject.SIGNAL_RUN_LAST,
|
||||||
'apply' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE, (GObject.TYPE_STRING,))
|
GObject.TYPE_NONE,
|
||||||
}
|
(GObject.TYPE_PYOBJECT,)),
|
||||||
|
'apply': (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,
|
||||||
|
(GObject.TYPE_STRING,))
|
||||||
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ import dbus
|
||||||
|
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
from gi.repository import Gtk, Gdk, GdkPixbuf # pylint: disable=E0611
|
from gi.repository import Gtk, GdkPixbuf # pylint: disable=E0611
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('mugshot')
|
logger = logging.getLogger('mugshot')
|
||||||
|
|
||||||
|
@ -44,52 +44,64 @@ libreoffice_prefs = os.path.join(home, '.config', 'libreoffice', '4', 'user',
|
||||||
pidgin_prefs = os.path.join(home, '.purple', 'prefs.xml')
|
pidgin_prefs = os.path.join(home, '.purple', 'prefs.xml')
|
||||||
faces_dir = '/usr/share/pixmaps/faces/'
|
faces_dir = '/usr/share/pixmaps/faces/'
|
||||||
|
|
||||||
|
|
||||||
def which(command):
|
def which(command):
|
||||||
'''Use the system command which to get the absolute path for the given
|
'''Use the system command which to get the absolute path for the given
|
||||||
command.'''
|
command.'''
|
||||||
command = subprocess.Popen(['which', command], \
|
command = subprocess.Popen(['which', command],
|
||||||
stdout=subprocess.PIPE).stdout.read().strip()
|
stdout=subprocess.PIPE).stdout.read().strip()
|
||||||
if command == '':
|
if command == '':
|
||||||
logger.debug('Command "%s" could not be found.' % command)
|
logger.debug('Command "%s" could not be found.' % command)
|
||||||
return None
|
return None
|
||||||
return command
|
return command
|
||||||
|
|
||||||
|
|
||||||
def has_running_process(name):
|
def has_running_process(name):
|
||||||
"""Check for a running process, return True if any listings are found."""
|
"""Check for a running process, return True if any listings are found."""
|
||||||
command = 'ps -ef | grep " %s" | grep -v "grep" | wc -l' % name
|
command = 'ps -ef | grep " %s" | grep -v "grep" | wc -l' % name
|
||||||
n = subprocess.Popen(command, stdout=subprocess.PIPE,
|
n = subprocess.Popen(command, stdout=subprocess.PIPE,
|
||||||
shell=True).stdout.read().strip()
|
shell=True).stdout.read().strip()
|
||||||
return int(n) > 0
|
return int(n) > 0
|
||||||
|
|
||||||
|
|
||||||
def has_gstreamer_camerabin_support():
|
def has_gstreamer_camerabin_support():
|
||||||
"""Return True if gstreamer1.0 camerabin element is available."""
|
"""Return True if gstreamer1.0 camerabin element is available."""
|
||||||
process = subprocess.Popen(["gst-inspect-1.0", "camerabin"],
|
process = subprocess.Popen(["gst-inspect-1.0", "camerabin"],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
has_support = process.returncode == 0
|
has_support = process.returncode == 0
|
||||||
if not has_support:
|
if not has_support:
|
||||||
logger.debug('camerabin element unavailable. Do you have gstreamer1.0-plugins-good installed?')
|
element = 'camerabin'
|
||||||
|
plugin = 'gstreamer1.0-plugins-good'
|
||||||
|
logger.debug('%s element unavailable. '
|
||||||
|
'Do you have %s installed?' % (element, plugin))
|
||||||
return has_support
|
return has_support
|
||||||
|
|
||||||
|
|
||||||
def has_gstreamer_camerasrc_support():
|
def has_gstreamer_camerasrc_support():
|
||||||
"""Return True if gstreamer1.0 v4l2src element is available."""
|
"""Return True if gstreamer1.0 v4l2src element is available."""
|
||||||
process = subprocess.Popen(["gst-inspect-1.0", "v4l2src"],
|
process = subprocess.Popen(["gst-inspect-1.0", "v4l2src"],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
process.communicate()
|
process.communicate()
|
||||||
has_support = process.returncode == 0
|
has_support = process.returncode == 0
|
||||||
if not has_support:
|
if not has_support:
|
||||||
logger.debug('v4l2src element unavailable. Do you have gstreamer1.0-plugins-good installed?')
|
element = 'v4l2src'
|
||||||
|
plugin = 'gstreamer1.0-plugins-good'
|
||||||
|
logger.debug('%s element unavailable. '
|
||||||
|
'Do you have %s installed?' % (element, plugin))
|
||||||
return has_support
|
return has_support
|
||||||
|
|
||||||
|
|
||||||
def get_camera_installed():
|
def get_camera_installed():
|
||||||
"""Return True if /dev/video0 exists."""
|
"""Return True if /dev/video0 exists."""
|
||||||
if not os.path.exists('/dev/video0'):
|
if not os.path.exists('/dev/video0'):
|
||||||
logger.debug('Camera not detected at /dev/video0')
|
logger.debug('Camera not detected at /dev/video0')
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def get_has_camera_support():
|
def get_has_camera_support():
|
||||||
"""Return True if cameras are fully supported by this application."""
|
"""Return True if cameras are fully supported by this application."""
|
||||||
if not get_camera_installed():
|
if not get_camera_installed():
|
||||||
|
@ -102,10 +114,12 @@ def get_has_camera_support():
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def detach_cb(menu, widget):
|
def detach_cb(menu, widget):
|
||||||
'''Detach a widget from its attached widget.'''
|
'''Detach a widget from its attached widget.'''
|
||||||
menu.detach()
|
menu.detach()
|
||||||
|
|
||||||
|
|
||||||
def get_entry_value(entry_widget):
|
def get_entry_value(entry_widget):
|
||||||
"""Get the value from one of the Mugshot entries."""
|
"""Get the value from one of the Mugshot entries."""
|
||||||
# Get the text from an entry, changing none to ''
|
# Get the text from an entry, changing none to ''
|
||||||
|
@ -113,10 +127,11 @@ def get_entry_value(entry_widget):
|
||||||
if value.lower() == 'none':
|
if value.lower() == 'none':
|
||||||
value = ''
|
value = ''
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def get_confirmation_dialog(parent, primary_message, secondary_message,
|
|
||||||
|
def get_confirmation_dialog(parent, primary_message, secondary_message,
|
||||||
icon_name=None):
|
icon_name=None):
|
||||||
"""Display a confirmation (yes/no) dialog configured with primary and
|
"""Display a confirmation (yes/no) dialog configured with primary and
|
||||||
secondary messages, as well as a custom icon if requested."""
|
secondary messages, as well as a custom icon if requested."""
|
||||||
dialog = Gtk.MessageDialog(parent, flags=0, type=Gtk.MessageType.QUESTION,
|
dialog = Gtk.MessageDialog(parent, flags=0, type=Gtk.MessageType.QUESTION,
|
||||||
buttons=Gtk.ButtonsType.YES_NO,
|
buttons=Gtk.ButtonsType.YES_NO,
|
||||||
|
@ -129,7 +144,8 @@ def get_confirmation_dialog(parent, primary_message, secondary_message,
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
return response == Gtk.ResponseType.YES
|
return response == Gtk.ResponseType.YES
|
||||||
|
|
||||||
|
|
||||||
def menu_position(self, menu, data=None, something_else=None):
|
def menu_position(self, menu, data=None, something_else=None):
|
||||||
'''Position a menu at the bottom of its attached widget'''
|
'''Position a menu at the bottom of its attached widget'''
|
||||||
widget = menu.get_attach_widget()
|
widget = menu.get_attach_widget()
|
||||||
|
@ -141,15 +157,17 @@ def menu_position(self, menu, data=None, something_else=None):
|
||||||
y = window_pos[1] + allocation.y + allocation.height
|
y = window_pos[1] + allocation.y + allocation.height
|
||||||
return (x, y, True)
|
return (x, y, True)
|
||||||
|
|
||||||
|
|
||||||
# See mugshot_lib.Window.py for more details about how this class works
|
# See mugshot_lib.Window.py for more details about how this class works
|
||||||
class MugshotWindow(Window):
|
class MugshotWindow(Window):
|
||||||
|
"""Mugshot GtkWindow"""
|
||||||
__gtype_name__ = "MugshotWindow"
|
__gtype_name__ = "MugshotWindow"
|
||||||
|
|
||||||
def finish_initializing(self, builder): # pylint: disable=E1002
|
def finish_initializing(self, builder): # pylint: disable=E1002
|
||||||
"""Set up the main window"""
|
"""Set up the main window"""
|
||||||
super(MugshotWindow, self).finish_initializing(builder)
|
super(MugshotWindow, self).finish_initializing(builder)
|
||||||
self.set_wmclass("Mugshot", "Mugshot")
|
self.set_wmclass("Mugshot", "Mugshot")
|
||||||
|
|
||||||
self.CameraDialog = CameraMugshotDialog
|
self.CameraDialog = CameraMugshotDialog
|
||||||
|
|
||||||
# User Image widgets
|
# User Image widgets
|
||||||
|
@ -159,9 +177,9 @@ class MugshotWindow(Window):
|
||||||
self.image_menu.attach_to_widget(self.image_button, detach_cb)
|
self.image_menu.attach_to_widget(self.image_button, detach_cb)
|
||||||
self.image_from_camera = builder.get_object('image_from_camera')
|
self.image_from_camera = builder.get_object('image_from_camera')
|
||||||
image_from_browse = builder.get_object('image_from_browse')
|
image_from_browse = builder.get_object('image_from_browse')
|
||||||
image_from_browse.set_visible( os.path.exists(faces_dir) and \
|
image_from_browse.set_visible(os.path.exists(faces_dir) and
|
||||||
len(os.listdir(faces_dir)) > 0 )
|
len(os.listdir(faces_dir)) > 0)
|
||||||
|
|
||||||
# Entry widgets (chfn)
|
# Entry widgets (chfn)
|
||||||
self.first_name_entry = builder.get_object('first_name')
|
self.first_name_entry = builder.get_object('first_name')
|
||||||
self.last_name_entry = builder.get_object('last_name')
|
self.last_name_entry = builder.get_object('last_name')
|
||||||
|
@ -170,11 +188,11 @@ class MugshotWindow(Window):
|
||||||
self.home_phone_entry = builder.get_object('home_phone')
|
self.home_phone_entry = builder.get_object('home_phone')
|
||||||
self.email_entry = builder.get_object('email')
|
self.email_entry = builder.get_object('email')
|
||||||
self.fax_entry = builder.get_object('fax')
|
self.fax_entry = builder.get_object('fax')
|
||||||
|
|
||||||
# Stock photo browser
|
# Stock photo browser
|
||||||
self.stock_browser = builder.get_object('stock_browser')
|
self.stock_browser = builder.get_object('stock_browser')
|
||||||
self.iconview = builder.get_object('stock_iconview')
|
self.iconview = builder.get_object('stock_iconview')
|
||||||
|
|
||||||
# File Chooser Dialog
|
# File Chooser Dialog
|
||||||
self.chooser = builder.get_object('filechooserdialog')
|
self.chooser = builder.get_object('filechooserdialog')
|
||||||
self.crop_center = builder.get_object('crop_center')
|
self.crop_center = builder.get_object('crop_center')
|
||||||
|
@ -186,12 +204,12 @@ class MugshotWindow(Window):
|
||||||
image_filter.set_name('Images')
|
image_filter.set_name('Images')
|
||||||
image_filter.add_mime_type('image/*')
|
image_filter.add_mime_type('image/*')
|
||||||
self.chooser.add_filter(image_filter)
|
self.chooser.add_filter(image_filter)
|
||||||
|
|
||||||
self.tmpfile = None
|
self.tmpfile = None
|
||||||
|
|
||||||
# Populate all of the widgets.
|
# Populate all of the widgets.
|
||||||
self.init_user_details()
|
self.init_user_details()
|
||||||
|
|
||||||
def init_user_details(self):
|
def init_user_details(self):
|
||||||
"""Initialize the user details entries and variables."""
|
"""Initialize the user details entries and variables."""
|
||||||
# Check for .face and set profile image.
|
# Check for .face and set profile image.
|
||||||
|
@ -202,7 +220,7 @@ class MugshotWindow(Window):
|
||||||
else:
|
else:
|
||||||
self.set_user_image(None)
|
self.set_user_image(None)
|
||||||
self.updated_image = None
|
self.updated_image = None
|
||||||
|
|
||||||
# Search /etc/passwd for the current user's details.
|
# Search /etc/passwd for the current user's details.
|
||||||
logger.debug('Getting user details from /etc/passwd')
|
logger.debug('Getting user details from /etc/passwd')
|
||||||
for line in open('/etc/passwd', 'r'):
|
for line in open('/etc/passwd', 'r'):
|
||||||
|
@ -211,7 +229,7 @@ class MugshotWindow(Window):
|
||||||
details = line.split(':')[4]
|
details = line.split(':')[4]
|
||||||
name, office, office_phone, home_phone = details.split(',', 3)
|
name, office, office_phone, home_phone = details.split(',', 3)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Expand the user's fullname into first, last, and initials.
|
# Expand the user's fullname into first, last, and initials.
|
||||||
try:
|
try:
|
||||||
first_name, last_name = name.split(' ', 1)
|
first_name, last_name = name.split(' ', 1)
|
||||||
|
@ -220,25 +238,27 @@ class MugshotWindow(Window):
|
||||||
first_name = name
|
first_name = name
|
||||||
last_name = ''
|
last_name = ''
|
||||||
initials = first_name[0]
|
initials = first_name[0]
|
||||||
|
|
||||||
# If the variables are defined as 'none', use blank for cleanliness.
|
# If the variables are defined as 'none', use blank for cleanliness.
|
||||||
if home_phone == 'none': home_phone = ''
|
if home_phone == 'none':
|
||||||
if office_phone == 'none': office_phone = ''
|
home_phone = ''
|
||||||
|
if office_phone == 'none':
|
||||||
|
office_phone = ''
|
||||||
|
|
||||||
# Get dconf settings
|
# Get dconf settings
|
||||||
logger.debug('Getting initials, email, and fax from dconf')
|
logger.debug('Getting initials, email, and fax from dconf')
|
||||||
if self.settings['initials'] != '':
|
if self.settings['initials'] != '':
|
||||||
initials = self.settings['initials']
|
initials = self.settings['initials']
|
||||||
email = self.settings['email']
|
email = self.settings['email']
|
||||||
fax = self.settings['fax']
|
fax = self.settings['fax']
|
||||||
|
|
||||||
# Set the class variables
|
# Set the class variables
|
||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
self.last_name = last_name
|
self.last_name = last_name
|
||||||
self.initials = initials
|
self.initials = initials
|
||||||
self.home_phone = home_phone
|
self.home_phone = home_phone
|
||||||
self.office_phone = office_phone
|
self.office_phone = office_phone
|
||||||
|
|
||||||
# Populate the GtkEntries.
|
# Populate the GtkEntries.
|
||||||
logger.debug('Populating entries')
|
logger.debug('Populating entries')
|
||||||
self.first_name_entry.set_text(self.first_name)
|
self.first_name_entry.set_text(self.first_name)
|
||||||
|
@ -248,7 +268,7 @@ class MugshotWindow(Window):
|
||||||
self.home_phone_entry.set_text(self.home_phone)
|
self.home_phone_entry.set_text(self.home_phone)
|
||||||
self.email_entry.set_text(email)
|
self.email_entry.set_text(email)
|
||||||
self.fax_entry.set_text(fax)
|
self.fax_entry.set_text(fax)
|
||||||
|
|
||||||
# = Mugshot Window ======================================================= #
|
# = Mugshot Window ======================================================= #
|
||||||
def set_user_image(self, filename=None):
|
def set_user_image(self, filename=None):
|
||||||
"""Scale and set the user profile image."""
|
"""Scale and set the user profile image."""
|
||||||
|
@ -264,83 +284,84 @@ class MugshotWindow(Window):
|
||||||
"""Allow only numbers and + in phone entry fields."""
|
"""Allow only numbers and + in phone entry fields."""
|
||||||
text = entry.get_text().strip()
|
text = entry.get_text().strip()
|
||||||
entry.set_text(''.join([i for i in text if i in '+0123456789']))
|
entry.set_text(''.join([i for i in text if i in '+0123456789']))
|
||||||
|
|
||||||
def on_apply_button_clicked(self, widget):
|
def on_apply_button_clicked(self, widget):
|
||||||
"""When the window Apply button is clicked, commit any relevant
|
"""When the window Apply button is clicked, commit any relevant
|
||||||
changes."""
|
changes."""
|
||||||
logger.debug('Applying changes...')
|
logger.debug('Applying changes...')
|
||||||
if self.get_chfn_details_updated():
|
if self.get_chfn_details_updated():
|
||||||
returns = self.save_chfn_details()
|
self.save_chfn_details()
|
||||||
|
|
||||||
if self.get_libreoffice_details_updated():
|
if self.get_libreoffice_details_updated():
|
||||||
self.set_libreoffice_data()
|
self.set_libreoffice_data()
|
||||||
|
|
||||||
if self.updated_image:
|
if self.updated_image:
|
||||||
self.save_image()
|
self.save_image()
|
||||||
|
|
||||||
self.save_gsettings()
|
self.save_gsettings()
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def save_gsettings(self):
|
def save_gsettings(self):
|
||||||
"""Save details to dconf (the ones not tracked by /etc/passwd)"""
|
"""Save details to dconf (the ones not tracked by /etc/passwd)"""
|
||||||
logger.debug('Saving details to dconf: /apps/mugshot')
|
logger.debug('Saving details to dconf: /apps/mugshot')
|
||||||
self.settings.set_string('initials',
|
self.settings.set_string('initials',
|
||||||
get_entry_value(self.initials_entry))
|
get_entry_value(self.initials_entry))
|
||||||
self.settings.set_string('email', get_entry_value(self.email_entry))
|
self.settings.set_string('email', get_entry_value(self.email_entry))
|
||||||
self.settings.set_string('fax', get_entry_value(self.fax_entry))
|
self.settings.set_string('fax', get_entry_value(self.fax_entry))
|
||||||
|
|
||||||
def entry_focus_next(self, widget):
|
def entry_focus_next(self, widget):
|
||||||
"""Focus the next available entry when pressing Enter."""
|
"""Focus the next available entry when pressing Enter."""
|
||||||
logger.debug('Entry activated, focusing next widget.')
|
logger.debug('Entry activated, focusing next widget.')
|
||||||
vbox = widget.get_parent().get_parent().get_parent().get_parent()
|
vbox = widget.get_parent().get_parent().get_parent().get_parent()
|
||||||
vbox.child_focus(Gtk.DirectionType.TAB_FORWARD)
|
vbox.child_focus(Gtk.DirectionType.TAB_FORWARD)
|
||||||
|
|
||||||
def on_cancel_button_clicked(self, widget):
|
def on_cancel_button_clicked(self, widget):
|
||||||
"""When the window cancel button is clicked, close the program."""
|
"""When the window cancel button is clicked, close the program."""
|
||||||
logger.debug('Cancel clicked, goodbye.')
|
logger.debug('Cancel clicked, goodbye.')
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
# = Image Button and Menu ================================================ #
|
# = Image Button and Menu ================================================ #
|
||||||
def on_image_button_clicked(self, widget):
|
def on_image_button_clicked(self, widget):
|
||||||
"""When the menu button is clicked, display the photo menu."""
|
"""When the menu button is clicked, display the photo menu."""
|
||||||
if widget.get_active():
|
if widget.get_active():
|
||||||
logger.debug('Show photo menu')
|
logger.debug('Show photo menu')
|
||||||
self.image_from_camera.set_visible(get_has_camera_support())
|
self.image_from_camera.set_visible(get_has_camera_support())
|
||||||
self.image_menu.popup(None, None, menu_position,
|
self.image_menu.popup(None, None, menu_position,
|
||||||
self.image_menu, 3,
|
self.image_menu, 3,
|
||||||
Gtk.get_current_event_time())
|
Gtk.get_current_event_time())
|
||||||
|
|
||||||
def on_image_menu_hide(self, widget):
|
def on_image_menu_hide(self, widget):
|
||||||
"""Untoggle the image button when the menu is hidden."""
|
"""Untoggle the image button when the menu is hidden."""
|
||||||
self.image_button.set_active(False)
|
self.image_button.set_active(False)
|
||||||
|
|
||||||
def on_camera_dialog_apply(self, widget, data=None):
|
def on_camera_dialog_apply(self, widget, data=None):
|
||||||
|
"""Commit changes when apply is clicked."""
|
||||||
self.updated_image = data
|
self.updated_image = data
|
||||||
self.set_user_image(data)
|
self.set_user_image(data)
|
||||||
|
|
||||||
def save_image(self):
|
def save_image(self):
|
||||||
"""Copy the updated image filename to ~/.face"""
|
"""Copy the updated image filename to ~/.face"""
|
||||||
# Check if the image has been updated.
|
# Check if the image has been updated.
|
||||||
if not self.updated_image:
|
if not self.updated_image:
|
||||||
logger.debug('Photo not updated, not saving changes.')
|
logger.debug('Photo not updated, not saving changes.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
face = os.path.expanduser('~/.face')
|
face = os.path.expanduser('~/.face')
|
||||||
|
|
||||||
# If the .face file already exists, remove it first.
|
# If the .face file already exists, remove it first.
|
||||||
logger.debug('Photo updated, saving changes.')
|
logger.debug('Photo updated, saving changes.')
|
||||||
if os.path.isfile(face):
|
if os.path.isfile(face):
|
||||||
os.remove(face)
|
os.remove(face)
|
||||||
|
|
||||||
# Copy the new file to ~/.face
|
# Copy the new file to ~/.face
|
||||||
shutil.copyfile(self.updated_image, face)
|
shutil.copyfile(self.updated_image, face)
|
||||||
self.set_pidgin_buddyicon(face)
|
self.set_pidgin_buddyicon(face)
|
||||||
self.updated_image = None
|
self.updated_image = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_pidgin_buddyicon(self, filename=None):
|
def set_pidgin_buddyicon(self, filename=None):
|
||||||
"""Sets the pidgin buddyicon to filename (usually ~/.face).
|
"""Sets the pidgin buddyicon to filename (usually ~/.face).
|
||||||
|
|
||||||
If pidgin is running, use the dbus interface, otherwise directly modify
|
If pidgin is running, use the dbus interface, otherwise directly modify
|
||||||
the XML file."""
|
the XML file."""
|
||||||
if not os.path.exists(pidgin_prefs):
|
if not os.path.exists(pidgin_prefs):
|
||||||
|
@ -358,19 +379,19 @@ class MugshotWindow(Window):
|
||||||
self.set_pidgin_buddyicon_xml(filename)
|
self.set_pidgin_buddyicon_xml(filename)
|
||||||
else:
|
else:
|
||||||
logger.debug('Reject: Not updating pidgin buddy icon')
|
logger.debug('Reject: Not updating pidgin buddy icon')
|
||||||
|
|
||||||
def set_pidgin_buddyicon_dbus(self, filename=None):
|
def set_pidgin_buddyicon_dbus(self, filename=None):
|
||||||
"""Set the pidgin buddy icon via dbus."""
|
"""Set the pidgin buddy icon via dbus."""
|
||||||
logger.debug('Updating pidgin buddy icon via dbus')
|
logger.debug('Updating pidgin buddy icon via dbus')
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
obj = bus.get_object("im.pidgin.purple.PurpleService",
|
obj = bus.get_object("im.pidgin.purple.PurpleService",
|
||||||
"/im/pidgin/purple/PurpleObject")
|
"/im/pidgin/purple/PurpleObject")
|
||||||
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
|
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
|
||||||
# To make the change instantly visible, set the icon to none first.
|
# To make the change instantly visible, set the icon to none first.
|
||||||
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', '')
|
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', '')
|
||||||
if filename:
|
if filename:
|
||||||
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', filename)
|
purple.PurplePrefsSetPath('/pidgin/accounts/buddyicon', filename)
|
||||||
|
|
||||||
def set_pidgin_buddyicon_xml(self, filename=None):
|
def set_pidgin_buddyicon_xml(self, filename=None):
|
||||||
"""Set the buddyicon used by pidgin to filename (via the xml file)."""
|
"""Set the buddyicon used by pidgin to filename (via the xml file)."""
|
||||||
# This is hacky, but a working implementation for now...
|
# This is hacky, but a working implementation for now...
|
||||||
|
@ -380,9 +401,9 @@ class MugshotWindow(Window):
|
||||||
if os.path.isfile(prefs_file):
|
if os.path.isfile(prefs_file):
|
||||||
for line in open(prefs_file):
|
for line in open(prefs_file):
|
||||||
if '<pref name=\'buddyicon\'' in line:
|
if '<pref name=\'buddyicon\'' in line:
|
||||||
new = line.split('value=')[0]
|
new = line.split('value=')[0]
|
||||||
if filename:
|
if filename:
|
||||||
new = new + 'value=\'%s\'/>\n' % filename
|
new = new + 'value=\'%s\'/>\n' % filename
|
||||||
else:
|
else:
|
||||||
new = new + 'value=\'\'/>\n'
|
new = new + 'value=\'\'/>\n'
|
||||||
tmp_buffer.append(new)
|
tmp_buffer.append(new)
|
||||||
|
@ -392,7 +413,7 @@ class MugshotWindow(Window):
|
||||||
for line in tmp_buffer:
|
for line in tmp_buffer:
|
||||||
write_prefs.write(line)
|
write_prefs.write(line)
|
||||||
write_prefs.close()
|
write_prefs.close()
|
||||||
|
|
||||||
# = chfn functions ============================================ #
|
# = chfn functions ============================================ #
|
||||||
def get_chfn_details_updated(self):
|
def get_chfn_details_updated(self):
|
||||||
"""Return True if chfn-related details have been modified."""
|
"""Return True if chfn-related details have been modified."""
|
||||||
|
@ -405,25 +426,25 @@ class MugshotWindow(Window):
|
||||||
return True
|
return True
|
||||||
logger.debug('chfn details have NOT been modified.')
|
logger.debug('chfn details have NOT been modified.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def save_chfn_details(self):
|
def save_chfn_details(self):
|
||||||
"""Commit changes to chfn-related details. For full name, changes must
|
"""Commit changes to chfn-related details. For full name, changes must
|
||||||
be performed as root. Other changes are done with the user password.
|
be performed as root. Other changes are done with the user password.
|
||||||
|
|
||||||
Return exit codes for 1) full name changes and 2) home/work phone
|
Return exit codes for 1) full name changes and 2) home/work phone
|
||||||
changes.
|
changes.
|
||||||
|
|
||||||
e.g. [0, 0] (both passed)"""
|
e.g. [0, 0] (both passed)"""
|
||||||
return_codes = []
|
return_codes = []
|
||||||
|
|
||||||
# Get the user's password
|
# Get the user's password
|
||||||
password = self.get_password()
|
password = self.get_password()
|
||||||
if not password:
|
if not password:
|
||||||
return return_codes
|
return return_codes
|
||||||
|
|
||||||
username = os.getenv('USER')
|
username = os.getenv('USER')
|
||||||
chfn = which('chfn')
|
chfn = which('chfn')
|
||||||
|
|
||||||
# Get each of the updated values.
|
# Get each of the updated values.
|
||||||
first_name = get_entry_value(self.first_name_entry)
|
first_name = get_entry_value(self.first_name_entry)
|
||||||
last_name = get_entry_value(self.last_name_entry)
|
last_name = get_entry_value(self.last_name_entry)
|
||||||
|
@ -435,7 +456,7 @@ class MugshotWindow(Window):
|
||||||
home_phone = get_entry_value(self.home_phone_entry)
|
home_phone = get_entry_value(self.home_phone_entry)
|
||||||
if home_phone == '':
|
if home_phone == '':
|
||||||
home_phone = 'none'
|
home_phone = 'none'
|
||||||
|
|
||||||
# Full name can only be modified by root. Try using sudo to modify.
|
# Full name can only be modified by root. Try using sudo to modify.
|
||||||
logger.debug('Attempting to set fullname with sudo chfn')
|
logger.debug('Attempting to set fullname with sudo chfn')
|
||||||
child = pexpect.spawn('sudo %s %s' % (chfn, username))
|
child = pexpect.spawn('sudo %s %s' % (chfn, username))
|
||||||
|
@ -449,9 +470,9 @@ class MugshotWindow(Window):
|
||||||
child.sendline('')
|
child.sendline('')
|
||||||
except pexpect.TIMEOUT:
|
except pexpect.TIMEOUT:
|
||||||
# Password was incorrect, or sudo rights not granted
|
# Password was incorrect, or sudo rights not granted
|
||||||
logger.debug('Timeout reached, password was incorrect or sudo ' \
|
logger.debug('Timeout reached, password was incorrect or sudo '
|
||||||
'rights not granted.')
|
'rights not granted.')
|
||||||
pass
|
pass
|
||||||
child.close()
|
child.close()
|
||||||
if child.exitstatus == 0:
|
if child.exitstatus == 0:
|
||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
|
@ -479,7 +500,7 @@ class MugshotWindow(Window):
|
||||||
self.home_phone = home_phone
|
self.home_phone = home_phone
|
||||||
return_codes.append(child.exitstatus)
|
return_codes.append(child.exitstatus)
|
||||||
return return_codes
|
return return_codes
|
||||||
|
|
||||||
# = LibreOffice ========================================================== #
|
# = LibreOffice ========================================================== #
|
||||||
def get_libreoffice_details_updated(self):
|
def get_libreoffice_details_updated(self):
|
||||||
"""Return True if LibreOffice settings need to be updated."""
|
"""Return True if LibreOffice settings need to be updated."""
|
||||||
|
@ -506,14 +527,14 @@ class MugshotWindow(Window):
|
||||||
return True
|
return True
|
||||||
logger.debug('LibreOffice details do not need to be updated.')
|
logger.debug('LibreOffice details do not need to be updated.')
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_libreoffice_data(self):
|
def get_libreoffice_data(self):
|
||||||
"""Get each of the preferences from the LibreOffice registymodifications
|
"""Get each of the preferences from the LibreOffice registymodifications
|
||||||
preferences file.
|
preferences file.
|
||||||
|
|
||||||
Return a dict with the details."""
|
Return a dict with the details."""
|
||||||
prefs_file = libreoffice_prefs
|
prefs_file = libreoffice_prefs
|
||||||
data = {'first_name': '', 'last_name': '', 'initials': '', 'email': '',
|
data = {'first_name': '', 'last_name': '', 'initials': '', 'email': '',
|
||||||
'home_phone': '', 'office_phone': '', 'fax': ''}
|
'home_phone': '', 'office_phone': '', 'fax': ''}
|
||||||
if os.path.isfile(prefs_file):
|
if os.path.isfile(prefs_file):
|
||||||
logger.debug('Getting settings from %s' % prefs_file)
|
logger.debug('Getting settings from %s' % prefs_file)
|
||||||
|
@ -545,7 +566,7 @@ class MugshotWindow(Window):
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def set_libreoffice_data(self):
|
def set_libreoffice_data(self):
|
||||||
"""Update the LibreOffice registymodifications preferences file."""
|
"""Update the LibreOffice registymodifications preferences file."""
|
||||||
prefs_file = libreoffice_prefs
|
prefs_file = libreoffice_prefs
|
||||||
|
@ -622,40 +643,61 @@ class MugshotWindow(Window):
|
||||||
open_prefs = open(prefs_file, 'w')
|
open_prefs = open(prefs_file, 'w')
|
||||||
for line in tmp_buffer:
|
for line in tmp_buffer:
|
||||||
open_prefs.write(line)
|
open_prefs.write(line)
|
||||||
|
|
||||||
if not first_name_updated:
|
if not first_name_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="givenname" oor:op="fuse"><value>%s</value></prop></item>\n' % first_name
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="givenname" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % first_name
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not last_name_updated:
|
if not last_name_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="sn" oor:op="fuse"><value>%s</value></prop></item>\n' % last_name
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="sn" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % last_name
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not initials_updated:
|
if not initials_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="initials" oor:op="fuse"><value>%s</value></prop></item>\n' % initials
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="initials" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % initials
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not email_updated:
|
if not email_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="mail" oor:op="fuse"><value>%s</value></prop></item>\n' % email
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="mail" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % email
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not home_phone_updated:
|
if not home_phone_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="homephone" oor:op="fuse"><value>%s</value></prop></item>\n' % home_phone
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="homephone" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % home_phone
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not office_phone_updated:
|
if not office_phone_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="telephonenumber" oor:op="fuse"><value>%s</value></prop></item>\n' % office_phone
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="telephonenumber" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % office_phone
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
if not fax_updated:
|
if not fax_updated:
|
||||||
string = '<item oor:path="/org.openoffice.UserProfile/Data"><prop oor:name="facsimiletelephonenumber" oor:op="fuse"><value>%s</value></prop></item>\n' % fax
|
string = \
|
||||||
|
'<item oor:path="/org.openoffice.UserProfile/Data">'
|
||||||
|
'<prop oor:name="facsimiletelephonenumber" oor:op="fuse">'
|
||||||
|
'<value>%s</value></prop></item>\n' % fax
|
||||||
open_prefs.write(string)
|
open_prefs.write(string)
|
||||||
open_prefs.write('</oor:items>')
|
open_prefs.write('</oor:items>')
|
||||||
open_prefs.close()
|
open_prefs.close()
|
||||||
else:
|
else:
|
||||||
logger.debug('Reject: Not updating.')
|
logger.debug('Reject: Not updating.')
|
||||||
|
|
||||||
# = Stock Browser ======================================================== #
|
# = Stock Browser ======================================================== #
|
||||||
def on_image_from_stock_activate(self, widget):
|
def on_image_from_stock_activate(self, widget):
|
||||||
"""When the 'Select image from stock' menu item is clicked, load and
|
"""When the 'Select image from stock' menu item is clicked, load and
|
||||||
display the stock photo browser."""
|
display the stock photo browser."""
|
||||||
self.load_stock_browser()
|
self.load_stock_browser()
|
||||||
self.stock_browser.show_all()
|
self.stock_browser.show_all()
|
||||||
|
|
||||||
def load_stock_browser(self):
|
def load_stock_browser(self):
|
||||||
"""Load the stock photo browser."""
|
"""Load the stock photo browser."""
|
||||||
# Check if the photos have already been loaded.
|
# Check if the photos have already been loaded.
|
||||||
|
@ -663,7 +705,7 @@ class MugshotWindow(Window):
|
||||||
if len(model) != 0:
|
if len(model) != 0:
|
||||||
logger.debug("Stock browser already loaded.")
|
logger.debug("Stock browser already loaded.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# If they have not, load each photo from /usr/share/pixmaps/faces.
|
# If they have not, load each photo from /usr/share/pixmaps/faces.
|
||||||
logger.debug("Loading stock browser photos.")
|
logger.debug("Loading stock browser photos.")
|
||||||
for filename in os.listdir('/usr/share/pixmaps/faces'):
|
for filename in os.listdir('/usr/share/pixmaps/faces'):
|
||||||
|
@ -672,24 +714,24 @@ class MugshotWindow(Window):
|
||||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(full_path)
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file(full_path)
|
||||||
scaled = pixbuf.scale_simple(90, 90, GdkPixbuf.InterpType.HYPER)
|
scaled = pixbuf.scale_simple(90, 90, GdkPixbuf.InterpType.HYPER)
|
||||||
model.append([full_path, scaled])
|
model.append([full_path, scaled])
|
||||||
|
|
||||||
def on_stock_iconview_selection_changed(self, widget):
|
def on_stock_iconview_selection_changed(self, widget):
|
||||||
"""Enable stock submission only when an item is selected."""
|
"""Enable stock submission only when an item is selected."""
|
||||||
selected_items = self.iconview.get_selected_items()
|
selected_items = self.iconview.get_selected_items()
|
||||||
self.builder.get_object('stock_ok').set_sensitive(
|
self.builder.get_object('stock_ok').set_sensitive(
|
||||||
len(selected_items) > 0)
|
len(selected_items) > 0)
|
||||||
|
|
||||||
def on_stock_browser_delete_event(self, widget, event):
|
def on_stock_browser_delete_event(self, widget, event):
|
||||||
"""Hide the stock browser instead of deleting it."""
|
"""Hide the stock browser instead of deleting it."""
|
||||||
widget.hide()
|
widget.hide()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_stock_cancel_clicked(self, widget):
|
def on_stock_cancel_clicked(self, widget):
|
||||||
"""Hide the stock browser when Cancel is clicked."""
|
"""Hide the stock browser when Cancel is clicked."""
|
||||||
self.stock_browser.hide()
|
self.stock_browser.hide()
|
||||||
|
|
||||||
def on_stock_ok_clicked(self, widget):
|
def on_stock_ok_clicked(self, widget):
|
||||||
"""When the stock browser OK button is clicked, get the currently
|
"""When the stock browser OK button is clicked, get the currently
|
||||||
selected photo and set it to the user profile image."""
|
selected photo and set it to the user profile image."""
|
||||||
selected_items = self.iconview.get_selected_items()
|
selected_items = self.iconview.get_selected_items()
|
||||||
if len(selected_items) != 0:
|
if len(selected_items) != 0:
|
||||||
|
@ -697,16 +739,16 @@ class MugshotWindow(Window):
|
||||||
path = int(selected_items[0].to_string())
|
path = int(selected_items[0].to_string())
|
||||||
filename = self.iconview.get_model()[path][0]
|
filename = self.iconview.get_model()[path][0]
|
||||||
logger.debug("Selected %s" % filename)
|
logger.debug("Selected %s" % filename)
|
||||||
|
|
||||||
# Update variables and widgets, then hide.
|
# Update variables and widgets, then hide.
|
||||||
self.set_user_image(filename)
|
self.set_user_image(filename)
|
||||||
self.updated_image = filename
|
self.updated_image = filename
|
||||||
self.stock_browser.hide()
|
self.stock_browser.hide()
|
||||||
|
|
||||||
def on_stock_iconview_item_activated(self, widget, path):
|
def on_stock_iconview_item_activated(self, widget, path):
|
||||||
"""Allow selecting a stock photo with Enter."""
|
"""Allow selecting a stock photo with Enter."""
|
||||||
self.on_stock_ok_clicked(widget)
|
self.on_stock_ok_clicked(widget)
|
||||||
|
|
||||||
# = Image Browser ======================================================== #
|
# = Image Browser ======================================================== #
|
||||||
def on_image_from_browse_activate(self, widget):
|
def on_image_from_browse_activate(self, widget):
|
||||||
"""Browse for a user profile image."""
|
"""Browse for a user profile image."""
|
||||||
|
@ -719,12 +761,14 @@ class MugshotWindow(Window):
|
||||||
self.tmpfile = tempfile.NamedTemporaryFile(delete=False)
|
self.tmpfile = tempfile.NamedTemporaryFile(delete=False)
|
||||||
self.tmpfile.close()
|
self.tmpfile.close()
|
||||||
self.updated_image = self.tmpfile.name
|
self.updated_image = self.tmpfile.name
|
||||||
self.filechooser_preview_pixbuf.savev(self.updated_image, "png", [], [])
|
self.filechooser_preview_pixbuf.savev(self.updated_image, "png",
|
||||||
|
[], [])
|
||||||
logger.debug("Selected %s" % self.updated_image)
|
logger.debug("Selected %s" % self.updated_image)
|
||||||
self.set_user_image(self.updated_image)
|
self.set_user_image(self.updated_image)
|
||||||
self.chooser.hide()
|
self.chooser.hide()
|
||||||
|
|
||||||
def on_filechooserdialog_update_preview(self, widget):
|
def on_filechooserdialog_update_preview(self, widget):
|
||||||
|
"""Update the preview image used in the file chooser."""
|
||||||
filename = widget.get_filename()
|
filename = widget.get_filename()
|
||||||
if not filename:
|
if not filename:
|
||||||
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
||||||
|
@ -733,47 +777,50 @@ class MugshotWindow(Window):
|
||||||
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
self.file_chooser_preview.set_from_icon_name('folder', 128)
|
||||||
return
|
return
|
||||||
filechooser_pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
filechooser_pixbuf = GdkPixbuf.Pixbuf.new_from_file(filename)
|
||||||
|
|
||||||
# Get the image dimensions.
|
# Get the image dimensions.
|
||||||
height = filechooser_pixbuf.get_height()
|
height = filechooser_pixbuf.get_height()
|
||||||
width = filechooser_pixbuf.get_width()
|
width = filechooser_pixbuf.get_width()
|
||||||
start_x = 0
|
start_x = 0
|
||||||
start_y = 0
|
start_y = 0
|
||||||
|
|
||||||
if self.crop_center.get_active():
|
if self.crop_center.get_active():
|
||||||
# Calculate a balanced center.
|
# Calculate a balanced center.
|
||||||
if width > height:
|
if width > height:
|
||||||
start_x = (width-height)/2
|
start_x = (width - height) / 2
|
||||||
width = height
|
width = height
|
||||||
else:
|
else:
|
||||||
start_y = (height-width)/2
|
start_y = (height - width) / 2
|
||||||
height = width
|
height = width
|
||||||
|
|
||||||
elif self.crop_left.get_active():
|
elif self.crop_left.get_active():
|
||||||
start_x = 0
|
start_x = 0
|
||||||
if width > height:
|
if width > height:
|
||||||
width = height
|
width = height
|
||||||
else:
|
else:
|
||||||
start_y = (height-width)/2
|
start_y = (height - width) / 2
|
||||||
height = width
|
height = width
|
||||||
elif self.crop_right.get_active():
|
elif self.crop_right.get_active():
|
||||||
if width > height:
|
if width > height:
|
||||||
start_x = width-height
|
start_x = width - height
|
||||||
width = height
|
width = height
|
||||||
else:
|
else:
|
||||||
start_y = (height-width)/2
|
start_y = (height - width) / 2
|
||||||
height = width
|
height = width
|
||||||
|
|
||||||
# Create a new cropped pixbuf.
|
# Create a new cropped pixbuf.
|
||||||
self.filechooser_preview_pixbuf = filechooser_pixbuf.new_subpixbuf(start_x, start_y, width, height)
|
self.filechooser_preview_pixbuf = \
|
||||||
|
filechooser_pixbuf.new_subpixbuf(start_x, start_y, width, height)
|
||||||
scaled = self.filechooser_preview_pixbuf.scale_simple(128, 128, GdkPixbuf.InterpType.HYPER)
|
|
||||||
|
scaled = self.filechooser_preview_pixbuf.scale_simple(128, 128,
|
||||||
|
GdkPixbuf.InterpType.HYPER)
|
||||||
self.file_chooser_preview.set_from_pixbuf(scaled)
|
self.file_chooser_preview.set_from_pixbuf(scaled)
|
||||||
|
|
||||||
def on_crop_changed(self, widget, data=None):
|
def on_crop_changed(self, widget, data=None):
|
||||||
|
"""Update the preview image when crop style is modified."""
|
||||||
if widget.get_active():
|
if widget.get_active():
|
||||||
self.on_filechooserdialog_update_preview(self.chooser)
|
self.on_filechooserdialog_update_preview(self.chooser)
|
||||||
|
|
||||||
# = Password Entry ======================================================= #
|
# = Password Entry ======================================================= #
|
||||||
def get_password(self):
|
def get_password(self):
|
||||||
"""Display a password dialog for authenticating to sudo and chfn."""
|
"""Display a password dialog for authenticating to sudo and chfn."""
|
||||||
|
@ -789,9 +836,8 @@ class MugshotWindow(Window):
|
||||||
return pw
|
return pw
|
||||||
logger.debug("Cancelled")
|
logger.debug("Cancelled")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def on_password_entry_changed(self, widget):
|
def on_password_entry_changed(self, widget):
|
||||||
"""Enable password submission only when password is not blank."""
|
"""Enable password submission only when password is not blank."""
|
||||||
self.builder.get_object('password_ok').set_sensitive(
|
self.builder.get_object('password_ok').set_sensitive(
|
||||||
len(widget.get_text()) > 0)
|
len(widget.get_text()) > 0)
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
'''Enhances builder connections, provides object to access glade objects'''
|
'''Enhances builder connections, provides object to access glade objects'''
|
||||||
|
|
||||||
from gi.repository import GObject, Gtk # pylint: disable=E0611
|
from gi.repository import GObject, Gtk # pylint: disable=E0611
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import functools
|
import functools
|
||||||
|
@ -46,6 +46,7 @@ class Builder(Gtk.Builder):
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""Initialize the builder."""
|
||||||
Gtk.Builder.__init__(self)
|
Gtk.Builder.__init__(self)
|
||||||
self.widgets = {}
|
self.widgets = {}
|
||||||
self.glade_handler_dict = {}
|
self.glade_handler_dict = {}
|
||||||
|
@ -120,7 +121,7 @@ class Builder(Gtk.Builder):
|
||||||
connection_dict = {}
|
connection_dict = {}
|
||||||
connection_dict.update(self.glade_handler_dict)
|
connection_dict.update(self.glade_handler_dict)
|
||||||
connection_dict.update(callback_handler_dict)
|
connection_dict.update(callback_handler_dict)
|
||||||
for item in connection_dict.items():
|
for item in list(connection_dict.items()):
|
||||||
if item[1] is None:
|
if item[1] is None:
|
||||||
# the handler is missing so reroute to default_handler
|
# the handler is missing so reroute to default_handler
|
||||||
handler = functools.partial(
|
handler = functools.partial(
|
||||||
|
@ -166,17 +167,19 @@ class Builder(Gtk.Builder):
|
||||||
class UiFactory():
|
class UiFactory():
|
||||||
''' provides an object with attributes as glade widgets'''
|
''' provides an object with attributes as glade widgets'''
|
||||||
def __init__(self, widget_dict):
|
def __init__(self, widget_dict):
|
||||||
|
"""Initialize the UiFactory."""
|
||||||
self._widget_dict = widget_dict
|
self._widget_dict = widget_dict
|
||||||
for (widget_name, widget) in widget_dict.items():
|
for (widget_name, widget) in list(widget_dict.items()):
|
||||||
setattr(self, widget_name, widget)
|
setattr(self, widget_name, widget)
|
||||||
|
|
||||||
# Mangle any non-usable names (like with spaces or dashes)
|
# Mangle any non-usable names (like with spaces or dashes)
|
||||||
# into pythonic ones
|
# into pythonic ones
|
||||||
cannot_message = """cannot bind ui.%s, name already exists
|
cannot_message = """cannot bind ui.%s, name already exists
|
||||||
consider using a pythonic name instead of design name '%s'"""
|
consider using a pythonic name instead of design name '%s'"""
|
||||||
consider_message = """consider using a pythonic name instead of design name '%s'"""
|
consider_message = """consider using a pythonic name instead of
|
||||||
|
design name '%s'"""
|
||||||
for (widget_name, widget) in widget_dict.items():
|
|
||||||
|
for (widget_name, widget) in list(widget_dict.items()):
|
||||||
pyname = make_pyname(widget_name)
|
pyname = make_pyname(widget_name)
|
||||||
if pyname != widget_name:
|
if pyname != widget_name:
|
||||||
if hasattr(self, pyname):
|
if hasattr(self, pyname):
|
||||||
|
@ -187,7 +190,7 @@ class UiFactory():
|
||||||
|
|
||||||
def iterator():
|
def iterator():
|
||||||
'''Support 'for o in self' '''
|
'''Support 'for o in self' '''
|
||||||
return iter(widget_dict.values())
|
return iter(list(widget_dict.values()))
|
||||||
setattr(self, '__iter__', iterator)
|
setattr(self, '__iter__', iterator)
|
||||||
|
|
||||||
def __getitem__(self, name):
|
def __getitem__(self, name):
|
||||||
|
@ -208,10 +211,11 @@ def make_pyname(name):
|
||||||
return pyname
|
return pyname
|
||||||
|
|
||||||
|
|
||||||
# Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we
|
# Until bug https://bugzilla.gnome.org/show_bug.cgi?id=652127 is fixed, we
|
||||||
# need to reimplement inspect.getmembers. GObject introspection doesn't
|
# need to reimplement inspect.getmembers. GObject introspection doesn't
|
||||||
# play nice with it.
|
# play nice with it.
|
||||||
def getmembers(obj, check):
|
def getmembers(obj, check):
|
||||||
|
"""Reimplementation of getmembers"""
|
||||||
members = []
|
members = []
|
||||||
for k in dir(obj):
|
for k in dir(obj):
|
||||||
try:
|
try:
|
||||||
|
@ -260,7 +264,7 @@ def auto_connect_by_name(callback_obj, builder):
|
||||||
|
|
||||||
callback_handler_dict = dict_from_callback_obj(callback_obj)
|
callback_handler_dict = dict_from_callback_obj(callback_obj)
|
||||||
|
|
||||||
for item in builder.widgets.items():
|
for item in list(builder.widgets.items()):
|
||||||
(widget_name, widget) = item
|
(widget_name, widget) = item
|
||||||
signal_ids = []
|
signal_ids = []
|
||||||
try:
|
try:
|
||||||
|
@ -296,7 +300,7 @@ def do_connect(item, signal_name, handler_names,
|
||||||
widget_name, widget = item
|
widget_name, widget = item
|
||||||
|
|
||||||
for handler_name in handler_names:
|
for handler_name in handler_names:
|
||||||
target = handler_name in callback_handler_dict.keys()
|
target = handler_name in list(callback_handler_dict.keys())
|
||||||
connection = (widget_name, signal_name, handler_name)
|
connection = (widget_name, signal_name, handler_name)
|
||||||
duplicate = connection in connections
|
duplicate = connection in connections
|
||||||
if target and not duplicate:
|
if target and not duplicate:
|
||||||
|
@ -312,7 +316,7 @@ def log_unconnected_functions(callback_handler_dict, connections):
|
||||||
|
|
||||||
connected_functions = [x[2] for x in connections]
|
connected_functions = [x[2] for x in connections]
|
||||||
|
|
||||||
handler_names = callback_handler_dict.keys()
|
handler_names = list(callback_handler_dict.keys())
|
||||||
unconnected = [x for x in handler_names if x.startswith('on_')]
|
unconnected = [x for x in handler_names if x.startswith('on_')]
|
||||||
|
|
||||||
for handler_name in connected_functions:
|
for handler_name in connected_functions:
|
||||||
|
|
|
@ -1,34 +1,36 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
### DO NOT EDIT THIS FILE ###
|
### DO NOT EDIT THIS FILE ###
|
||||||
|
|
||||||
from gi.repository import Gtk # pylint: disable=E0611
|
from gi.repository import Gtk # pylint: disable=E0611
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('mugshot_lib')
|
logger = logging.getLogger('mugshot_lib')
|
||||||
|
|
||||||
from . helpers import get_builder, show_uri, get_help_uri
|
from . helpers import get_builder, show_uri, get_help_uri
|
||||||
|
|
||||||
|
|
||||||
class CameraDialog(Gtk.Dialog):
|
class CameraDialog(Gtk.Dialog):
|
||||||
|
"""Camera Dialog"""
|
||||||
__gtype_name__ = "CameraDialog"
|
__gtype_name__ = "CameraDialog"
|
||||||
|
|
||||||
def __new__(cls):
|
def __new__(cls):
|
||||||
"""Special static method that's automatically called by Python when
|
"""Special static method that's automatically called by Python when
|
||||||
constructing a new instance of this class.
|
constructing a new instance of this class.
|
||||||
|
|
||||||
Returns a fully instantiated PreferencesDialog object.
|
Returns a fully instantiated PreferencesDialog object.
|
||||||
"""
|
"""
|
||||||
builder = get_builder('CameraMugshotDialog')
|
builder = get_builder('CameraMugshotDialog')
|
||||||
|
@ -43,7 +45,7 @@ class CameraDialog(Gtk.Dialog):
|
||||||
and creating a PreferencesDialog object with it in order to
|
and creating a PreferencesDialog object with it in order to
|
||||||
finish initializing the start of the new PerferencesMugshotDialog
|
finish initializing the start of the new PerferencesMugshotDialog
|
||||||
instance.
|
instance.
|
||||||
|
|
||||||
Put your initialization code in here and leave __init__ undefined.
|
Put your initialization code in here and leave __init__ undefined.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -54,8 +56,9 @@ class CameraDialog(Gtk.Dialog):
|
||||||
# code for other initialization actions should be added here
|
# code for other initialization actions should be added here
|
||||||
|
|
||||||
def on_btn_close_clicked(self, widget, data=None):
|
def on_btn_close_clicked(self, widget, data=None):
|
||||||
|
"""Destroy the dialog when closed."""
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def on_btn_help_clicked(self, widget, data=None):
|
def on_btn_help_clicked(self, widget, data=None):
|
||||||
|
"""Show the help dialog when Help is clicked."""
|
||||||
show_uri(self, "ghelp:%s" % get_help_uri('preferences'))
|
show_uri(self, "ghelp:%s" % get_help_uri('preferences'))
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,22 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
### DO NOT EDIT THIS FILE ###
|
### DO NOT EDIT THIS FILE ###
|
||||||
|
|
||||||
from gi.repository import Gio, Gtk # pylint: disable=E0611
|
from gi.repository import Gio, Gtk # pylint: disable=E0611
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('mugshot_lib')
|
logger = logging.getLogger('mugshot_lib')
|
||||||
|
|
||||||
|
@ -24,12 +24,13 @@ import os
|
||||||
|
|
||||||
from . helpers import get_builder, show_uri, get_help_uri
|
from . helpers import get_builder, show_uri, get_help_uri
|
||||||
|
|
||||||
# This class is meant to be subclassed by MugshotWindow. It provides
|
|
||||||
# common functions and some boilerplate.
|
|
||||||
class Window(Gtk.Window):
|
class Window(Gtk.Window):
|
||||||
|
"""This class is meant to be subclassed by MugshotWindow. It provides
|
||||||
|
common functions and some boilerplate."""
|
||||||
__gtype_name__ = "Window"
|
__gtype_name__ = "Window"
|
||||||
|
|
||||||
# To construct a new instance of this method, the following notable
|
# To construct a new instance of this method, the following notable
|
||||||
# methods are called in this order:
|
# methods are called in this order:
|
||||||
# __new__(cls)
|
# __new__(cls)
|
||||||
# __init__(self)
|
# __init__(self)
|
||||||
|
@ -38,11 +39,11 @@ class Window(Gtk.Window):
|
||||||
#
|
#
|
||||||
# For this reason, it's recommended you leave __init__ empty and put
|
# For this reason, it's recommended you leave __init__ empty and put
|
||||||
# your initialization code in finish_initializing
|
# your initialization code in finish_initializing
|
||||||
|
|
||||||
def __new__(cls):
|
def __new__(cls):
|
||||||
"""Special static method that's automatically called by Python when
|
"""Special static method that's automatically called by Python when
|
||||||
constructing a new instance of this class.
|
constructing a new instance of this class.
|
||||||
|
|
||||||
Returns a fully instantiated BaseMugshotWindow object.
|
Returns a fully instantiated BaseMugshotWindow object.
|
||||||
"""
|
"""
|
||||||
builder = get_builder('MugshotWindow')
|
builder = get_builder('MugshotWindow')
|
||||||
|
@ -60,15 +61,16 @@ 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.CameraDialog = None # class
|
||||||
self.camera_dialog = None # instance
|
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)
|
||||||
|
|
||||||
def on_help_activate(self, widget, data=None):
|
def on_help_activate(self, widget, data=None):
|
||||||
|
"""Show the Help documentation when Help is clicked."""
|
||||||
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):
|
def on_menu_camera_activate(self, widget, data=None):
|
||||||
"""Display the camera window for mugshot."""
|
"""Display the camera window for mugshot."""
|
||||||
if self.camera_dialog is not None:
|
if self.camera_dialog is not None:
|
||||||
|
@ -76,8 +78,7 @@ class Window(Gtk.Window):
|
||||||
self.camera_dialog.show()
|
self.camera_dialog.show()
|
||||||
elif self.CameraDialog is not None:
|
elif self.CameraDialog is not None:
|
||||||
logger.debug('create new camera_dialog')
|
logger.debug('create new camera_dialog')
|
||||||
self.camera_dialog = self.CameraDialog() # pylint: disable=E1102
|
self.camera_dialog = self.CameraDialog() # pylint: disable=E1102
|
||||||
#self.camera_dialog.connect('destroy', self.on_camera_dialog_destroyed)
|
|
||||||
self.camera_dialog.connect('apply', self.on_camera_dialog_apply)
|
self.camera_dialog.connect('apply', self.on_camera_dialog_apply)
|
||||||
self.camera_dialog.show()
|
self.camera_dialog.show()
|
||||||
|
|
||||||
|
@ -89,5 +90,6 @@ class Window(Gtk.Window):
|
||||||
Gtk.main_quit()
|
Gtk.main_quit()
|
||||||
|
|
||||||
def on_preferences_changed(self, settings, key, data=None):
|
def on_preferences_changed(self, settings, key, data=None):
|
||||||
logger.debug('preference changed: %s = %s' % (key, str(settings.get_value(key))))
|
"""Log preference updates."""
|
||||||
|
logger.debug('preference changed: %s = %s' %
|
||||||
|
(key, str(settings.get_value(key))))
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
|
@ -23,12 +23,11 @@ import os
|
||||||
from . mugshotconfig import get_data_file
|
from . mugshotconfig import get_data_file
|
||||||
from . Builder import Builder
|
from . Builder import Builder
|
||||||
|
|
||||||
from locale import gettext as _
|
|
||||||
|
|
||||||
def get_builder(builder_file_name):
|
def get_builder(builder_file_name):
|
||||||
"""Return a fully-instantiated Gtk.Builder instance from specified ui
|
"""Return a fully-instantiated Gtk.Builder instance from specified ui
|
||||||
file
|
file
|
||||||
|
|
||||||
:param builder_file_name: The name of the builder file, without extension.
|
:param builder_file_name: The name of the builder file, without extension.
|
||||||
Assumed to be in the 'ui' directory under the data path.
|
Assumed to be in the 'ui' directory under the data path.
|
||||||
"""
|
"""
|
||||||
|
@ -43,25 +42,31 @@ def get_builder(builder_file_name):
|
||||||
return builder
|
return builder
|
||||||
|
|
||||||
|
|
||||||
# Owais Lone : To get quick access to icons and stuff.
|
|
||||||
def get_media_file(media_file_name):
|
def get_media_file(media_file_name):
|
||||||
|
"""Retrieve the filename for the specified file."""
|
||||||
media_filename = get_data_file('media', '%s' % (media_file_name,))
|
media_filename = get_data_file('media', '%s' % (media_file_name,))
|
||||||
if not os.path.exists(media_filename):
|
if not os.path.exists(media_filename):
|
||||||
media_filename = None
|
media_filename = None
|
||||||
|
|
||||||
return "file:///"+media_filename
|
return "file:///" + media_filename
|
||||||
|
|
||||||
|
|
||||||
class NullHandler(logging.Handler):
|
class NullHandler(logging.Handler):
|
||||||
|
"""Handle NULL"""
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
|
"""Do not emit anything."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def set_up_logging(opts):
|
def set_up_logging(opts):
|
||||||
|
"""Set up the logging formatter."""
|
||||||
# add a handler to prevent basicConfig
|
# add a handler to prevent basicConfig
|
||||||
root = logging.getLogger()
|
root = logging.getLogger()
|
||||||
null_handler = NullHandler()
|
null_handler = NullHandler()
|
||||||
root.addHandler(null_handler)
|
root.addHandler(null_handler)
|
||||||
|
|
||||||
formatter = logging.Formatter("%(levelname)s:%(name)s: %(funcName)s() '%(message)s'")
|
formatter = logging.Formatter("%(levelname)s:%(name)s:"
|
||||||
|
" %(funcName)s() '%(message)s'")
|
||||||
|
|
||||||
logger = logging.getLogger('mugshot')
|
logger = logging.getLogger('mugshot')
|
||||||
logger_sh = logging.StreamHandler()
|
logger_sh = logging.StreamHandler()
|
||||||
|
@ -80,7 +85,9 @@ def set_up_logging(opts):
|
||||||
if opts.verbose > 1:
|
if opts.verbose > 1:
|
||||||
lib_logger.setLevel(logging.DEBUG)
|
lib_logger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
|
|
||||||
def get_help_uri(page=None):
|
def get_help_uri(page=None):
|
||||||
|
"""Get the URI to be used for Help."""
|
||||||
# help_uri from source tree - default language
|
# help_uri from source tree - default language
|
||||||
here = os.path.dirname(__file__)
|
here = os.path.dirname(__file__)
|
||||||
help_uri = os.path.abspath(os.path.join(here, '..', 'help', 'C'))
|
help_uri = os.path.abspath(os.path.join(here, '..', 'help', 'C'))
|
||||||
|
@ -95,11 +102,14 @@ def get_help_uri(page=None):
|
||||||
|
|
||||||
return help_uri
|
return help_uri
|
||||||
|
|
||||||
|
|
||||||
def show_uri(parent, link):
|
def show_uri(parent, link):
|
||||||
from gi.repository import Gtk # pylint: disable=E0611
|
"""Open the URI."""
|
||||||
|
from gi.repository import Gtk # pylint: disable=E0611
|
||||||
screen = parent.get_screen()
|
screen = parent.get_screen()
|
||||||
Gtk.show_uri(screen, link, Gtk.get_current_event_time())
|
Gtk.show_uri(screen, link, Gtk.get_current_event_time())
|
||||||
|
|
||||||
|
|
||||||
def alias(alternative_function_name):
|
def alias(alternative_function_name):
|
||||||
'''see http://www.drdobbs.com/web-development/184406073#l9'''
|
'''see http://www.drdobbs.com/web-development/184406073#l9'''
|
||||||
def decorator(function):
|
def decorator(function):
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
||||||
### BEGIN LICENSE
|
### BEGIN LICENSE
|
||||||
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
# Copyright (C) 2013 Sean Davis <smd.seandavis@gmail.com>
|
||||||
# This program is free software: you can redistribute it and/or modify it
|
# 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
|
# under the terms of the GNU General Public License version 3, as published
|
||||||
# by the Free Software Foundation.
|
# by the Free Software Foundation.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
||||||
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
||||||
# PURPOSE. See the GNU General Public License for more details.
|
# PURPOSE. See the GNU General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License along
|
# You should have received a copy of the GNU General Public License along
|
||||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
### END LICENSE
|
### END LICENSE
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ __version__ = '0.1'
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from locale import gettext as _
|
|
||||||
|
|
||||||
class project_path_not_found(Exception):
|
class project_path_not_found(Exception):
|
||||||
"""Raised when we can't find the project directory."""
|
"""Raised when we can't find the project directory."""
|
||||||
|
@ -66,4 +65,5 @@ def get_data_path():
|
||||||
|
|
||||||
|
|
||||||
def get_version():
|
def get_version():
|
||||||
|
"""Return the program version."""
|
||||||
return __version__
|
return __version__
|
||||||
|
|
Loading…
Reference in New Issue