mirror of
https://github.com/boltgolt/howdy.git
synced 2024-09-12 09:41:18 +02:00
Merge branch 'gkt-ui' into dev
This commit is contained in:
commit
2c14735ac4
26 changed files with 2133 additions and 11 deletions
2
debian/control
vendored
2
debian/control
vendored
|
@ -10,7 +10,7 @@ Package: howdy
|
|||
Homepage: https://github.com/boltgolt/howdy
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, curl|wget, python3, python3-pip, python3-dev, python3-setuptools, libpam-python, libopencv-dev, cmake
|
||||
Recommends: libatlas-base-dev | libopenblas-dev | liblapack-dev, streamer
|
||||
Recommends: libatlas-base-dev | libopenblas-dev | liblapack-dev, streamer, howdy-gtk
|
||||
Suggests: nvidia-cuda-dev (>= 7.5)
|
||||
Description: Howdy: Windows Hello style authentication for Linux.
|
||||
Use your built-in IR emitters and camera in combination with face recognition
|
||||
|
|
5
howdy-gtk/debian/changelog
Normal file
5
howdy-gtk/debian/changelog
Normal file
|
@ -0,0 +1,5 @@
|
|||
howdy-gtk (0.0.1) xenial; urgency=medium
|
||||
|
||||
* Initial testing release with sticky authentication ui
|
||||
|
||||
-- boltgolt <boltgolt@gmail.com> Thu, 03 Dec 2020 00:08:49 +0200
|
1
howdy-gtk/debian/compat
Normal file
1
howdy-gtk/debian/compat
Normal file
|
@ -0,0 +1 @@
|
|||
10
|
13
howdy-gtk/debian/control
Normal file
13
howdy-gtk/debian/control
Normal file
|
@ -0,0 +1,13 @@
|
|||
Source: howdy-gtk
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Standards-Version: 3.9.7
|
||||
Build-Depends: python, dh-python, devscripts, dh-make, debhelper, fakeroot
|
||||
Maintainer: boltgolt <boltgolt@gmail.com>
|
||||
Vcs-Git: https://github.com/boltgolt/howdy
|
||||
|
||||
Package: howdy-gtk
|
||||
Homepage: https://github.com/boltgolt/howdy
|
||||
Architecture: all
|
||||
Depends: ${misc:Depends}, curl|wget, python3, python3-pip, python3-dev, python-gtk2, python-gtk2-dev, cmake
|
||||
Description: Optional UI package for Howdy, written in Gtk
|
21
howdy-gtk/debian/copyright
Normal file
21
howdy-gtk/debian/copyright
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020 boltgolt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
1
howdy-gtk/debian/howdy-gtk.links
Normal file
1
howdy-gtk/debian/howdy-gtk.links
Normal file
|
@ -0,0 +1 @@
|
|||
/usr/lib/howdy-gtk/init.py /usr/bin/howdy-gtk
|
2
howdy-gtk/debian/howdy-gtk.lintian-overrides
Normal file
2
howdy-gtk/debian/howdy-gtk.lintian-overrides
Normal file
|
@ -0,0 +1,2 @@
|
|||
# W: Don't require ugly linebreaks in last 5 chars
|
||||
howdy: debian-changelog-line-too-long
|
1
howdy-gtk/debian/install
Normal file
1
howdy-gtk/debian/install
Normal file
|
@ -0,0 +1 @@
|
|||
src/. /usr/lib/howdy-gtk
|
2
howdy-gtk/debian/postinst
Normal file
2
howdy-gtk/debian/postinst
Normal file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
pip3 install elevate
|
8
howdy-gtk/debian/rules
Executable file
8
howdy-gtk/debian/rules
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/make -f
|
||||
DH_VERBOSE = 1
|
||||
|
||||
DPKG_EXPORT_BUILDFLAGS = 1
|
||||
include /usr/share/dpkg/default.mk
|
||||
|
||||
%:
|
||||
dh $@
|
1
howdy-gtk/debian/source/format
Normal file
1
howdy-gtk/debian/source/format
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (native)
|
8
howdy-gtk/debian/source/options
Normal file
8
howdy-gtk/debian/source/options
Normal file
|
@ -0,0 +1,8 @@
|
|||
tar-ignore = ".git"
|
||||
tar-ignore = ".gitignore"
|
||||
tar-ignore = ".github"
|
||||
tar-ignore = "README.md"
|
||||
tar-ignore = ".travis.yml"
|
||||
tar-ignore = "fedora"
|
||||
tar-ignore = "opensuse"
|
||||
tar-ignore = "archlinux"
|
157
howdy-gtk/src/authsticky.py
Normal file
157
howdy-gtk/src/authsticky.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
# Shows a floating window when authenticating
|
||||
import cairo
|
||||
import gi
|
||||
import signal
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Make sure we have the libs we need
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
|
||||
# Import them
|
||||
from gi.repository import Gtk as gtk
|
||||
from gi.repository import Gdk as gdk
|
||||
from gi.repository import GObject as gobject
|
||||
|
||||
# Set window size constants
|
||||
windowWidth = 400
|
||||
windowHeight = 100
|
||||
|
||||
# Set default messages to show in the popup
|
||||
message = "Starting up... "
|
||||
subtext = ""
|
||||
|
||||
|
||||
class StickyWindow(gtk.Window):
|
||||
def __init__(self):
|
||||
"""Initialize the sticky window"""
|
||||
# Make the class a GTK window
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
# Set the title of the window
|
||||
self.set_title("Howdy Authentication UI")
|
||||
|
||||
# Set a bunch of options to make the window stick and be on top of everything
|
||||
self.stick()
|
||||
self.set_gravity(gdk.Gravity.STATIC)
|
||||
self.set_resizable(False)
|
||||
self.set_keep_above(True)
|
||||
self.set_app_paintable(True)
|
||||
self.set_skip_pager_hint(True)
|
||||
self.set_skip_taskbar_hint(True)
|
||||
self.set_can_focus(False)
|
||||
self.set_can_default(False)
|
||||
self.set_focus(None)
|
||||
self.set_type_hint(gdk.WindowTypeHint.NOTIFICATION)
|
||||
self.set_decorated(False)
|
||||
|
||||
# Listen for a window redraw
|
||||
self.connect("draw", self.draw)
|
||||
# Listen for a force close or click event and exit
|
||||
self.connect("destroy", self.exit)
|
||||
self.connect("delete_event", self.exit)
|
||||
self.connect("button-press-event", self.exit)
|
||||
|
||||
# Create a GDK drawing, restricts the window size
|
||||
darea = gtk.DrawingArea()
|
||||
darea.set_size_request(windowWidth, windowHeight)
|
||||
self.add(darea)
|
||||
|
||||
# Get the default screen
|
||||
screen = gdk.Screen.get_default()
|
||||
visual = screen.get_rgba_visual()
|
||||
if visual and screen.is_composited():
|
||||
self.set_visual(visual)
|
||||
|
||||
# Move the window to the center top of the default window, where a webcam usually is
|
||||
self.move((screen.get_width() / 2) - (windowWidth / 2), 0)
|
||||
|
||||
# Show window and force a resize again
|
||||
self.show_all()
|
||||
self.resize(windowWidth, windowHeight)
|
||||
|
||||
# Add a timeout to catch input passed from compare.py
|
||||
gobject.timeout_add(100, self.catch_stdin)
|
||||
|
||||
# Start GTK main loop
|
||||
gtk.main()
|
||||
|
||||
def draw(self, widget, ctx):
|
||||
"""Draw the UI"""
|
||||
# Change cursor to the kill icon
|
||||
self.get_window().set_cursor(gdk.Cursor(gdk.CursorType.PIRATE))
|
||||
|
||||
# Draw a semi transparent background
|
||||
ctx.set_source_rgba(0, 0, 0, .7)
|
||||
ctx.set_operator(cairo.OPERATOR_SOURCE)
|
||||
ctx.paint()
|
||||
ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
|
||||
# Get absolute or relative logo path
|
||||
path = "/usr/lib/howdy-gtk/logo.png"
|
||||
if not os.access(path, os.R_OK):
|
||||
path = "./logo.png"
|
||||
|
||||
# Create image and calculate scale size based on image size
|
||||
image_surface = cairo.ImageSurface.create_from_png(path)
|
||||
ratio = float(windowHeight - 20) / float(image_surface.get_height())
|
||||
|
||||
# Position and draw the logo
|
||||
ctx.translate(15, 10)
|
||||
ctx.scale(ratio, ratio)
|
||||
ctx.set_source_surface(image_surface)
|
||||
ctx.paint()
|
||||
|
||||
# Calculate main message positioning, as the text is heigher if there's a subtext
|
||||
if subtext:
|
||||
ctx.move_to(380, 145)
|
||||
else:
|
||||
ctx.move_to(380, 170)
|
||||
|
||||
# Draw the main message
|
||||
ctx.set_source_rgba(255, 255, 255, .9)
|
||||
ctx.set_font_size(80)
|
||||
ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
||||
ctx.show_text(message)
|
||||
|
||||
# Draw the subtext if there is one
|
||||
if subtext:
|
||||
ctx.move_to(380, 210)
|
||||
ctx.set_source_rgba(230, 230, 230, .8)
|
||||
ctx.set_font_size(40)
|
||||
ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
||||
ctx.show_text(subtext)
|
||||
|
||||
def catch_stdin(self):
|
||||
"""Catch input from stdin and redraw"""
|
||||
global message, subtext
|
||||
|
||||
# Wait for a line on stdin
|
||||
comm = sys.stdin.readline()[:-1]
|
||||
|
||||
# If the line is not empty
|
||||
if comm:
|
||||
# Parse a message
|
||||
if comm[0] == "M":
|
||||
message = comm[2:]
|
||||
# Parse subtext
|
||||
if comm[0] == "S":
|
||||
subtext = comm[2:]
|
||||
|
||||
# Redraw the ui
|
||||
self.queue_draw()
|
||||
|
||||
# Fire this function again in 10ms, as we're waiting on IO in readline anyway
|
||||
gobject.timeout_add(10, self.catch_stdin)
|
||||
|
||||
def exit(self, widget, context):
|
||||
"""Cleanly exit"""
|
||||
gtk.main_quit()
|
||||
|
||||
|
||||
# Make sure we quit on a SIGINT
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# Open the GTK window
|
||||
window = StickyWindow()
|
12
howdy-gtk/src/i18n.py
Normal file
12
howdy-gtk/src/i18n.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Support file for translations
|
||||
|
||||
# Import modules
|
||||
import gettext
|
||||
import os
|
||||
|
||||
# Get the right translation based on locale, falling back to base if none found
|
||||
translation = gettext.translation("gtk", localedir=os.path.join(os.path.dirname(__file__), "locales"), fallback=True)
|
||||
translation.install()
|
||||
|
||||
# Export translation function as _
|
||||
_ = translation.gettext
|
8
howdy-gtk/src/init.py
Executable file
8
howdy-gtk/src/init.py
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
# Opens auth ui if requested, otherwise starts normal ui
|
||||
import sys
|
||||
|
||||
if "--start-auth-ui" in sys.argv:
|
||||
import authsticky
|
||||
else:
|
||||
import window
|
BIN
howdy-gtk/src/logo.png
Normal file
BIN
howdy-gtk/src/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
BIN
howdy-gtk/src/logo_about.png
Normal file
BIN
howdy-gtk/src/logo_about.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
512
howdy-gtk/src/main.glade
Normal file
512
howdy-gtk/src/main.glade
Normal file
|
@ -0,0 +1,512 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface domain="howdy">
|
||||
<requires lib="gtk+" version="3.10"/>
|
||||
<object class="GtkImage" id="iconadd">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="iconadduser">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="stock">gtk-add</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="icondelete">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="stock">gtk-delete</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="mainwindow">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="title" translatable="yes" context="Window title">Howdy Configuration</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="icon">logo.png</property>
|
||||
<child>
|
||||
<object class="GtkNotebook" id="notebook">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="margin_left">2</property>
|
||||
<property name="tab_pos">left</property>
|
||||
<property name="show_border">False</property>
|
||||
<signal name="switch-page" handler="on_page_switch" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="box3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="spacing">5</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="userlabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="label" translatable="yes">Showing saved models for:</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkComboBoxText" id="userlist">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<signal name="changed" handler="on_user_change" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="adduserbutton">
|
||||
<property name="label" translatable="yes">Add new user</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">15</property>
|
||||
<property name="image">iconadduser</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_user_add" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="modellistbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkSeparator" id="separator1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">8</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="addbutton">
|
||||
<property name="label">Add</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="image">iconadd</property>
|
||||
<property name="xalign">0.5899999737739563</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_model_add" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="deletebutton">
|
||||
<property name="label">Delete</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_right">11</property>
|
||||
<property name="image">icondelete</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_model_delete" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="modelstab">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="label" translatable="yes">Models</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Camera ID:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="videoid">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="selectable">True</property>
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="label" translatable="yes">Real resolution:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="videores">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="label" translatable="yes">Used resolution:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="videoresused">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="label" translatable="yes">Recorder:</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">6</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="videorecorder">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="selectable">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">7</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEventBox" id="opencvbox">
|
||||
<property name="width_request">300</property>
|
||||
<property name="height_request">300</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="opencvimage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="stock">gtk-execute</property>
|
||||
<property name="icon_size">6</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="videotab">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Video</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">20</property>
|
||||
<property name="xpad">12</property>
|
||||
<property name="pixbuf">logo_about.png</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">Howdy</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label" translatable="yes">Facial authentication for Linux</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_top">15</property>
|
||||
<property name="margin_bottom">35</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">3</property>
|
||||
<property name="label" translatable="yes"><a href="https://github.com/boltgolt/howdy">Open GitHub link</a> </property>
|
||||
<property name="use_markup">True</property>
|
||||
<property name="track_visited_links">False</property>
|
||||
<attributes>
|
||||
<attribute name="underline" value="True"/>
|
||||
<attribute name="foreground" value="#2929ababe2e2"/>
|
||||
<attribute name="underline-color" value="#2929ababe2e2"/>
|
||||
</attributes>
|
||||
<signal name="activate-link" handler="on_about_link" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">3</property>
|
||||
<property name="label" translatable="yes"><a href="https://www.buymeacoffee.com/boltgolt">Donate to the project</a> </property>
|
||||
<property name="use_markup">True</property>
|
||||
<attributes>
|
||||
<attribute name="underline" value="True"/>
|
||||
<attribute name="foreground" value="#2929ababe2e2"/>
|
||||
<attribute name="underline-color" value="#2929ababe2e2"/>
|
||||
</attributes>
|
||||
<signal name="activate-link" handler="on_about_link" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<object class="GtkLabel" id="abouttab">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label" translatable="yes">About</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
<property name="tab_fill">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="tab">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
697
howdy-gtk/src/onboarding.glade
Normal file
697
howdy-gtk/src/onboarding.glade
Normal file
|
@ -0,0 +1,697 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.18.3 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.12"/>
|
||||
<object class="GtkImage" id="iconcancel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">4</property>
|
||||
<property name="stock">gtk-cancel</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="iconfinish">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_right">5</property>
|
||||
<property name="stock">gtk-apply</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="iconforward">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">4</property>
|
||||
<property name="stock">gtk-go-forward</property>
|
||||
</object>
|
||||
<object class="GtkImage" id="iconscan">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="stock">gtk-media-play</property>
|
||||
</object>
|
||||
<object class="GtkWindow" id="onboardingwindow">
|
||||
<property name="width_request">500</property>
|
||||
<property name="height_request">400</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="title" translatable="yes">Welcome to Howdy</property>
|
||||
<property name="window_position">center</property>
|
||||
<property name="icon">logo.png</property>
|
||||
<property name="type_hint">menu</property>
|
||||
<property name="gravity">center</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="slidecontainer">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="resize_mode">immediate</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide5">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label9">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="label" translatable="yes">Setup is done!</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label10">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">20</property>
|
||||
<property name="label" translatable="yes">We're done! Howdy is now active on this computer. Try doing anything you would normally have to type your password for to authenticate, like running a command with sudo.
|
||||
|
||||
You can open the Howdy Configurator later on to change more advanced settings or add additional models. Press Finish below to close this window and open the Howdy Configurator. </property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide4">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label11">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="label" translatable="yes">Setting a certainty policy</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label12">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">40</property>
|
||||
<property name="label" translatable="yes">Because of changes in angles, distance, and other factors a face match is never exactly the same as the stored face model. On this page you can set how strict Howdy should be.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">60</property>
|
||||
<property name="margin_right">60</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="radiofast">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0.50999999046325684</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">radiobalanced</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label13">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Fast</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label14">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Allows more fuzzy matches,
|
||||
but speeds up the scanning process greatly.</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="radiobalanced">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label15">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Balanced</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label16">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Still relatively quick detection,
|
||||
but might not log you in when further away.</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRadioButton" id="radiosecure">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">False</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="active">True</property>
|
||||
<property name="draw_indicator">True</property>
|
||||
<property name="group">radiobalanced</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">5</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label17">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">Secure</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label18">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
<property name="label" translatable="yes">The slightly safer option,
|
||||
but will take much longer to authenticate you</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">False</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide3">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="label" translatable="yes">Adding a face model</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label5">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">20</property>
|
||||
<property name="label" translatable="yes">To authenticate you Howdy needs to save a model of your face to recognise you. Press the Scan button below to start the facial scan.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="scanbutton">
|
||||
<property name="label" translatable="yes">Start face scan</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="is_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">50</property>
|
||||
<property name="margin_right">50</property>
|
||||
<property name="image">iconscan</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="image_position">right</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="on_scanbutton_click" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="label" translatable="yes">Configuring webcam</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label3">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">20</property>
|
||||
<property name="label" translatable="yes">Howdy will search your system automatically for any available cameras, so make sure your webcam is connected. After detection a list of usable webcams will be shown. Pick the one you want to use and click Next.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="devicelistbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="opacity">0.89000000000000001</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="loadinglabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">15</property>
|
||||
<property name="label" translatable="yes">Testing your webcams, please wait...</property>
|
||||
<attributes>
|
||||
<attribute name="style" value="italic"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide1">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label8">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="label" translatable="yes">Downloading data files</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="margin_bottom">20</property>
|
||||
<property name="label" translatable="yes">Howdy needs three pre trained facial recognition datasets to be able to recognise you, which will be downloaded now. You can see the download progress below.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkEventBox" id="downloadeventbox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="downloadoutputlabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="label" translatable="yes">Starting download...</property>
|
||||
<property name="justify">center</property>
|
||||
<attributes>
|
||||
<attribute name="foreground" value="#f3f3f3f3f3f3"/>
|
||||
<attribute name="background" value="#000000000000"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="slide0">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">20</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<property name="xpad">7</property>
|
||||
<property name="ypad">13</property>
|
||||
<property name="pixbuf">logo_about.png</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label6">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_top">5</property>
|
||||
<property name="label" translatable="yes">Welcome to Howdy!</property>
|
||||
<attributes>
|
||||
<attribute name="scale" value="2"/>
|
||||
</attributes>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="margin_left">20</property>
|
||||
<property name="margin_right">20</property>
|
||||
<property name="margin_top">10</property>
|
||||
<property name="label" translatable="yes">This wizard will walk you through the setup process and automatically configure Howdy for you. Press next to continue.</property>
|
||||
<property name="justify">center</property>
|
||||
<property name="wrap">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="navigationbar">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="margin_bottom">10</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="cancelbutton">
|
||||
<property name="label" translatable="yes">Cancel</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_left">10</property>
|
||||
<property name="image">iconcancel</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="exit" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="nextbutton">
|
||||
<property name="label" translatable="yes">Next</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="has_focus">True</property>
|
||||
<property name="is_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="image">iconforward</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="image_position">right</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="go_next_slide" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="finishbutton">
|
||||
<property name="label" translatable="yes">Finish setup</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="receives_default">True</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="margin_right">10</property>
|
||||
<property name="image">iconfinish</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="always_show_image">True</property>
|
||||
<signal name="clicked" handler="exit" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">8</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
288
howdy-gtk/src/onboarding.py
Normal file
288
howdy-gtk/src/onboarding.py
Normal file
|
@ -0,0 +1,288 @@
|
|||
import sys
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
from i18n import _
|
||||
|
||||
from gi.repository import Gtk as gtk
|
||||
from gi.repository import Gdk as gdk
|
||||
from gi.repository import GObject as gobject
|
||||
from gi.repository import Pango as pango
|
||||
|
||||
|
||||
class OnboardingWindow(gtk.Window):
|
||||
def __init__(self):
|
||||
"""Initialize the sticky window"""
|
||||
# Make the class a GTK window
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self.connect("destroy", self.exit)
|
||||
self.connect("delete_event", self.exit)
|
||||
|
||||
self.builder = gtk.Builder()
|
||||
self.builder.add_from_file("./onboarding.glade")
|
||||
self.builder.connect_signals(self)
|
||||
|
||||
self.window = self.builder.get_object("onboardingwindow")
|
||||
self.nextbutton = self.builder.get_object("nextbutton")
|
||||
|
||||
self.slides = [
|
||||
self.builder.get_object("slide0"),
|
||||
self.builder.get_object("slide1"),
|
||||
self.builder.get_object("slide2"),
|
||||
self.builder.get_object("slide3"),
|
||||
self.builder.get_object("slide4"),
|
||||
self.builder.get_object("slide5")
|
||||
]
|
||||
|
||||
self.window.show_all()
|
||||
self.window.resize(500, 400)
|
||||
|
||||
self.window.current_slide = 0
|
||||
|
||||
# Start GTK main loop
|
||||
gtk.main()
|
||||
|
||||
def go_next_slide(self, button=None):
|
||||
self.nextbutton.set_sensitive(False)
|
||||
|
||||
self.slides[self.window.current_slide].hide()
|
||||
self.slides[self.window.current_slide + 1].show()
|
||||
self.window.current_slide += 1
|
||||
|
||||
if self.window.current_slide == 1:
|
||||
self.execute_slide1()
|
||||
elif self.window.current_slide == 2:
|
||||
gobject.timeout_add(10, self.execute_slide2)
|
||||
elif self.window.current_slide == 3:
|
||||
self.execute_slide3()
|
||||
elif self.window.current_slide == 4:
|
||||
self.execute_slide4()
|
||||
elif self.window.current_slide == 5:
|
||||
self.execute_slide5()
|
||||
|
||||
def execute_slide1(self):
|
||||
self.downloadoutputlabel = self.builder.get_object("downloadoutputlabel")
|
||||
eventbox = self.builder.get_object("downloadeventbox")
|
||||
eventbox.modify_bg(gtk.StateType.NORMAL, gdk.Color(red=0, green=0, blue=0))
|
||||
|
||||
if os.path.exists("/lib/security/howdy/dlib-data/shape_predictor_5_face_landmarks.dat"):
|
||||
self.downloadoutputlabel.set_text(_("Datafiles have already been downloaded!\nClick Next to continue"))
|
||||
self.enable_next()
|
||||
return
|
||||
|
||||
self.proc = subprocess.Popen("./install.sh", stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, cwd="/lib/security/howdy/dlib-data")
|
||||
|
||||
self.download_lines = []
|
||||
self.read_download_line()
|
||||
|
||||
def read_download_line(self):
|
||||
line = self.proc.stdout.readline()
|
||||
self.download_lines.append(line.decode("utf-8"))
|
||||
|
||||
print("install.sh output:")
|
||||
print(line.decode("utf-8"))
|
||||
|
||||
if len(self.download_lines) > 10:
|
||||
self.download_lines.pop(0)
|
||||
|
||||
self.downloadoutputlabel.set_text(" ".join(self.download_lines))
|
||||
|
||||
if line:
|
||||
gobject.timeout_add(10, self.read_download_line)
|
||||
return
|
||||
|
||||
# Wait for the process to finish and check the status code
|
||||
if self.proc.wait(5) != 0:
|
||||
self.show_error(_("Error while downloading datafiles"), " ".join(self.download_lines))
|
||||
|
||||
self.downloadoutputlabel.set_text(_("Done!\nClick Next to continue"))
|
||||
self.enable_next()
|
||||
|
||||
def execute_slide2(self):
|
||||
def is_gray(frame):
|
||||
for row in frame:
|
||||
for pixel in row:
|
||||
if not pixel[0] == pixel[1] == pixel[2]:
|
||||
return False
|
||||
return True
|
||||
|
||||
try:
|
||||
import cv2
|
||||
except Exception:
|
||||
self.show_error(_("Error while importing OpenCV2"), _("Try reinstalling cv2"))
|
||||
|
||||
device_ids = os.listdir("/dev/v4l/by-path")
|
||||
device_rows = []
|
||||
|
||||
if not device_ids:
|
||||
self.show_error(_("No webcams found on system"), _("Please configure your camera yourself if you are sure a compatible camera is connected"))
|
||||
|
||||
# Loop though all devices
|
||||
for dev in device_ids:
|
||||
time.sleep(.5)
|
||||
|
||||
# The full path to the device is the default name
|
||||
device_path = "/dev/v4l/by-path/" + dev
|
||||
device_name = dev
|
||||
|
||||
# Get the udevadm details to try to get a better name
|
||||
udevadm = subprocess.check_output(["udevadm info -r --query=all -n " + device_path], shell=True).decode("utf-8")
|
||||
|
||||
# Loop though udevadm to search for a better name
|
||||
for line in udevadm.split("\n"):
|
||||
# Match it and encase it in quotes
|
||||
re_name = re.search('product.*=(.*)$', line, re.IGNORECASE)
|
||||
if re_name:
|
||||
device_name = re_name.group(1)
|
||||
|
||||
try:
|
||||
capture = cv2.VideoCapture(device_path)
|
||||
capture.grab()
|
||||
ret, frame = capture.read()
|
||||
except Exception:
|
||||
device_rows.append([device_name, device_path, -9, _("No, camera can't be opened")])
|
||||
continue
|
||||
|
||||
if not is_gray(frame):
|
||||
device_rows.append([device_name, device_path, -5, _("No, not an infrared camera")])
|
||||
continue
|
||||
|
||||
time.sleep(.2)
|
||||
|
||||
ret, frame = capture.read()
|
||||
|
||||
if not is_gray(frame):
|
||||
device_rows.append([device_name, device_path, -5, _("No, not an infrared camera")])
|
||||
continue
|
||||
|
||||
device_rows.append([device_name, device_path, 5, _("Yes, compatible infrared camera")])
|
||||
|
||||
capture.release()
|
||||
|
||||
device_rows = sorted(device_rows, key=lambda k: -k[2])
|
||||
|
||||
self.loadinglabel = self.builder.get_object("loadinglabel")
|
||||
self.devicelistbox = self.builder.get_object("devicelistbox")
|
||||
|
||||
self.treeview = gtk.TreeView()
|
||||
self.treeview.set_vexpand(True)
|
||||
|
||||
# Set the coloums
|
||||
for i, column in enumerate([_("Camera identifier or path"), _("Recommended")]):
|
||||
cell = gtk.CellRendererText()
|
||||
cell.set_property("ellipsize", pango.EllipsizeMode.END)
|
||||
col = gtk.TreeViewColumn(column, cell, text=i)
|
||||
self.treeview.append_column(col)
|
||||
|
||||
# Add the treeview
|
||||
self.devicelistbox.add(self.treeview)
|
||||
|
||||
# Create a datamodel
|
||||
self.listmodel = gtk.ListStore(str, str, str)
|
||||
|
||||
for device in device_rows:
|
||||
self.listmodel.append([device[0], device[3], device[1]])
|
||||
|
||||
self.treeview.set_model(self.listmodel)
|
||||
self.treeview.set_cursor(0)
|
||||
|
||||
self.loadinglabel.hide()
|
||||
self.treeview.show()
|
||||
self.enable_next()
|
||||
|
||||
def execute_slide3(self):
|
||||
selection = self.treeview.get_selection()
|
||||
(listmodel, rowlist) = selection.get_selected_rows()
|
||||
|
||||
if len(rowlist) != 1:
|
||||
self.show_error(_("Error selecting camera"))
|
||||
|
||||
device_path = listmodel.get_value(listmodel.get_iter(rowlist[0]), 2)
|
||||
self.proc = subprocess.Popen("howdy set device_path " + device_path, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||
|
||||
self.window.set_focus(self.builder.get_object("scanbutton"))
|
||||
|
||||
def on_scanbutton_click(self, button):
|
||||
status = self.proc.wait(2)
|
||||
|
||||
# if status != 0:
|
||||
# self.show_error(_("Error setting camera path"), _("Please set the camera path manually"))
|
||||
|
||||
self.dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL)
|
||||
self.dialog.set_title(_("Creating Model"))
|
||||
self.dialog.props.text = _("Please look directly into the camera")
|
||||
self.dialog.show_all()
|
||||
|
||||
# Wait a bit to allow the user to read the dialog
|
||||
gobject.timeout_add(600, self.run_add)
|
||||
|
||||
def run_add(self):
|
||||
status, output = subprocess.getstatusoutput(["howdy add -y"])
|
||||
|
||||
print("howdy add output:")
|
||||
print(output)
|
||||
|
||||
self.dialog.destroy()
|
||||
|
||||
if status != 0:
|
||||
self.show_error(_("Can't save face model"), output)
|
||||
|
||||
gobject.timeout_add(10, self.go_next_slide)
|
||||
|
||||
def execute_slide4(self):
|
||||
self.enable_next()
|
||||
|
||||
def execute_slide5(self):
|
||||
radio_buttons = self.builder.get_object("radiobalanced").get_group()
|
||||
radio_selected = False
|
||||
radio_certanty = 5.0
|
||||
|
||||
for button in radio_buttons:
|
||||
if button.get_active():
|
||||
radio_selected = gtk.Buildable.get_name(button)
|
||||
|
||||
if not radio_selected:
|
||||
self.show_error(_("Error reading radio buttons"))
|
||||
elif radio_selected == "radiofast":
|
||||
radio_certanty = 4.2
|
||||
elif radio_selected == "radiobalanced":
|
||||
radio_certanty = 3.5
|
||||
elif radio_selected == "radiosecure":
|
||||
radio_certanty = 2.2
|
||||
|
||||
self.proc = subprocess.Popen("howdy set certainty " + str(radio_certanty), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
|
||||
|
||||
self.nextbutton.hide()
|
||||
self.builder.get_object("cancelbutton").hide()
|
||||
|
||||
finishbutton = self.builder.get_object("finishbutton")
|
||||
finishbutton.show()
|
||||
self.window.set_focus(finishbutton)
|
||||
|
||||
status = self.proc.wait(2)
|
||||
|
||||
if status != 0:
|
||||
self.show_error(_("Error setting certainty"), _("Certainty is set to the default value, Howdy setup is complete"))
|
||||
|
||||
def enable_next(self):
|
||||
self.nextbutton.set_sensitive(True)
|
||||
self.window.set_focus(self.nextbutton)
|
||||
|
||||
def show_error(self, error, secon=""):
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, type=gtk.MessageType.ERROR, buttons=gtk.ButtonsType.CLOSE)
|
||||
dialog.set_title(_("Howdy Error"))
|
||||
dialog.props.text = error
|
||||
dialog.format_secondary_text(secon)
|
||||
|
||||
dialog.run()
|
||||
|
||||
dialog.destroy()
|
||||
self.exit()
|
||||
|
||||
def exit(self, widget=None):
|
||||
"""Cleanly exit"""
|
||||
gtk.main_quit()
|
||||
sys.exit(0)
|
118
howdy-gtk/src/tab_models.py
Normal file
118
howdy-gtk/src/tab_models.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
import subprocess
|
||||
import time
|
||||
|
||||
from i18n import _
|
||||
from gi.repository import Gtk as gtk
|
||||
|
||||
|
||||
def on_user_change(self, select):
|
||||
self.active_user = select.get_active_text()
|
||||
self.load_model_list()
|
||||
|
||||
|
||||
def on_user_add(self, button):
|
||||
# Open question dialog
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, type=gtk.MessageType.QUESTION, buttons=gtk.ButtonsType.OK_CANCEL)
|
||||
dialog.set_title(_("Confirm User Creation"))
|
||||
dialog.props.text = _("Please enter the username of the user you want to add to Howdy")
|
||||
|
||||
# Create the input field
|
||||
entry = gtk.Entry()
|
||||
|
||||
# Add a label to ask for a model name
|
||||
hbox = gtk.HBox()
|
||||
hbox.pack_start(gtk.Label(_("Username:")), False, 5, 5)
|
||||
hbox.pack_end(entry, True, True, 5)
|
||||
|
||||
# Add the box and show the dialog
|
||||
dialog.vbox.pack_end(hbox, True, True, 0)
|
||||
dialog.show_all()
|
||||
|
||||
# Show dialog
|
||||
response = dialog.run()
|
||||
|
||||
entered_user = entry.get_text()
|
||||
dialog.destroy()
|
||||
|
||||
if response == gtk.ResponseType.OK:
|
||||
self.userlist.append_text(entered_user)
|
||||
self.userlist.set_active(self.userlist.items)
|
||||
self.userlist.items += 1
|
||||
|
||||
self.active_user = entered_user
|
||||
self.load_model_list()
|
||||
|
||||
|
||||
def on_model_add(self, button):
|
||||
# Open question dialog
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, type=gtk.MessageType.QUESTION, buttons=gtk.ButtonsType.OK_CANCEL)
|
||||
dialog.set_title(_("Confirm Model Creation"))
|
||||
dialog.props.text = _("Please enter a name for the new model, 24 characters max")
|
||||
|
||||
# Create the input field
|
||||
entry = gtk.Entry()
|
||||
|
||||
# Add a label to ask for a model name
|
||||
hbox = gtk.HBox()
|
||||
hbox.pack_start(gtk.Label(_("Model name:")), False, 5, 5)
|
||||
hbox.pack_end(entry, True, True, 5)
|
||||
|
||||
# Add the box and show the dialog
|
||||
dialog.vbox.pack_end(hbox, True, True, 0)
|
||||
dialog.show_all()
|
||||
|
||||
# Show dialog
|
||||
response = dialog.run()
|
||||
|
||||
entered_name = entry.get_text()
|
||||
dialog.destroy()
|
||||
|
||||
if response == gtk.ResponseType.OK:
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL)
|
||||
dialog.set_title(_("Creating Model"))
|
||||
dialog.props.text = _("Please look directly into the camera")
|
||||
dialog.show_all()
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
status, output = subprocess.getstatusoutput(["howdy add -y -U " + self.active_user + " '" + entered_name + "'"])
|
||||
|
||||
dialog.destroy()
|
||||
|
||||
if status != 0:
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, type=gtk.MessageType.ERROR, buttons=gtk.ButtonsType.CLOSE)
|
||||
dialog.set_title(_("Howdy Error"))
|
||||
dialog.props.text = _("Error while adding model, error code {}: \n\n").format(str(status))
|
||||
dialog.format_secondary_text(output)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
self.load_model_list()
|
||||
|
||||
|
||||
def on_model_delete(self, button):
|
||||
selection = self.treeview.get_selection()
|
||||
(listmodel, rowlist) = selection.get_selected_rows()
|
||||
|
||||
if len(rowlist) == 1:
|
||||
id = listmodel.get_value(listmodel.get_iter(rowlist[0]), 0)
|
||||
name = listmodel.get_value(listmodel.get_iter(rowlist[0]), 2)
|
||||
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, buttons=gtk.ButtonsType.OK_CANCEL)
|
||||
dialog.set_title(_("Confirm Model Deletion"))
|
||||
dialog.props.text = _("Are you sure you want to delete model {id} ({name})?").format(id=id, name=name)
|
||||
response = dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
if response == gtk.ResponseType.OK:
|
||||
status, output = subprocess.getstatusoutput(["howdy remove " + id + " -y -U " + self.active_user])
|
||||
|
||||
if status != 0:
|
||||
dialog = gtk.MessageDialog(parent=self, flags=gtk.DialogFlags.MODAL, type=gtk.MessageType.ERROR, buttons=gtk.ButtonsType.CLOSE)
|
||||
dialog.set_title(_("Howdy Error"))
|
||||
dialog.props.text = _("Error while deleting model, error code {}: \n\n").format(status)
|
||||
dialog.format_secondary_text(output)
|
||||
dialog.run()
|
||||
dialog.destroy()
|
||||
|
||||
self.load_model_list()
|
79
howdy-gtk/src/tab_video.py
Normal file
79
howdy-gtk/src/tab_video.py
Normal file
|
@ -0,0 +1,79 @@
|
|||
import configparser
|
||||
|
||||
from i18n import _
|
||||
|
||||
from gi.repository import Gtk as gtk
|
||||
from gi.repository import Gdk as gdk
|
||||
from gi.repository import GdkPixbuf as pixbuf
|
||||
from gi.repository import GObject as gobject
|
||||
|
||||
MAX_HEIGHT = 300
|
||||
MAX_WIDTH = 300
|
||||
|
||||
|
||||
def on_page_switch(self, notebook, page, page_num):
|
||||
if page_num == 1:
|
||||
path = "/dev/video1"
|
||||
|
||||
try:
|
||||
self.config = configparser.ConfigParser()
|
||||
self.config.read("/lib/security/howdy/config.ini")
|
||||
except Exception:
|
||||
print(_("Can't open camera"))
|
||||
|
||||
try:
|
||||
# if not self.cv2:
|
||||
import cv2
|
||||
self.cv2 = cv2
|
||||
except Exception:
|
||||
print(_("Can't import OpenCV2"))
|
||||
|
||||
try:
|
||||
self.capture = cv2.VideoCapture(self.config.get("video", "device_path"))
|
||||
except Exception:
|
||||
print(_("Can't open camera"))
|
||||
|
||||
opencvbox = self.builder.get_object("opencvbox")
|
||||
opencvbox.modify_bg(gtk.StateType.NORMAL, gdk.Color(red=0, green=0, blue=0))
|
||||
|
||||
height = self.capture.get(self.cv2.CAP_PROP_FRAME_HEIGHT) or 1
|
||||
width = self.capture.get(self.cv2.CAP_PROP_FRAME_WIDTH) or 1
|
||||
|
||||
self.scaling_factor = (MAX_HEIGHT / height) or 1
|
||||
|
||||
if width * self.scaling_factor > MAX_WIDTH:
|
||||
self.scaling_factor = (MAX_WIDTH / width) or 1
|
||||
|
||||
config_height = self.config.getfloat("video", "max_height", fallback=0.0)
|
||||
config_scaling = (config_height / height) or 1
|
||||
|
||||
self.builder.get_object("videoid").set_text(path.split("/")[-1])
|
||||
self.builder.get_object("videores").set_text(str(int(width)) + "x" + str(int(height)))
|
||||
self.builder.get_object("videoresused").set_text(str(int(width * config_scaling)) + "x" + str(int(height * config_scaling)))
|
||||
self.builder.get_object("videorecorder").set_text(self.config.get("video", "recording_plugin", fallback=_("Unknown")))
|
||||
|
||||
gobject.timeout_add(10, self.capture_frame)
|
||||
|
||||
elif self.capture is not None:
|
||||
self.capture.release()
|
||||
self.capture = None
|
||||
|
||||
|
||||
def capture_frame(self):
|
||||
if self.capture is None:
|
||||
return
|
||||
|
||||
ret, frame = self.capture.read()
|
||||
|
||||
frame = self.cv2.resize(frame, None, fx=self.scaling_factor, fy=self.scaling_factor, interpolation=self.cv2.INTER_AREA)
|
||||
|
||||
retval, buffer = self.cv2.imencode(".png", frame)
|
||||
|
||||
loader = pixbuf.PixbufLoader()
|
||||
loader.write(buffer)
|
||||
loader.close()
|
||||
buffer = loader.get_pixbuf()
|
||||
|
||||
self.opencvimage.set_from_pixbuf(buffer)
|
||||
|
||||
gobject.timeout_add(20, self.capture_frame)
|
137
howdy-gtk/src/window.py
Normal file
137
howdy-gtk/src/window.py
Normal file
|
@ -0,0 +1,137 @@
|
|||
# Opens and controls main ui window
|
||||
import gi
|
||||
import signal
|
||||
import sys
|
||||
import os
|
||||
import elevate
|
||||
import subprocess
|
||||
|
||||
from i18n import _
|
||||
|
||||
# Make sure we have the libs we need
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("Gdk", "3.0")
|
||||
|
||||
# Import them
|
||||
from gi.repository import Gtk as gtk
|
||||
|
||||
|
||||
class MainWindow(gtk.Window):
|
||||
def __init__(self):
|
||||
"""Initialize the sticky window"""
|
||||
# Make the class a GTK window
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
self.connect("destroy", self.exit)
|
||||
self.connect("delete_event", self.exit)
|
||||
|
||||
self.builder = gtk.Builder()
|
||||
self.builder.add_from_file("./main.glade")
|
||||
self.builder.connect_signals(self)
|
||||
|
||||
self.window = self.builder.get_object("mainwindow")
|
||||
self.userlist = self.builder.get_object("userlist")
|
||||
self.modellistbox = self.builder.get_object("modellistbox")
|
||||
self.opencvimage = self.builder.get_object("opencvimage")
|
||||
|
||||
# Init capture for video tab
|
||||
self.capture = None
|
||||
|
||||
# Create a treeview that will list the model data
|
||||
self.treeview = gtk.TreeView()
|
||||
self.treeview.set_vexpand(True)
|
||||
|
||||
# Set the coloums
|
||||
for i, column in enumerate([_("ID"), _("Created"), _("Label")]):
|
||||
col = gtk.TreeViewColumn(column, gtk.CellRendererText(), text=i)
|
||||
self.treeview.append_column(col)
|
||||
|
||||
# Add the treeview
|
||||
self.modellistbox.add(self.treeview)
|
||||
|
||||
filelist = os.listdir("/lib/security/howdy/models")
|
||||
self.active_user = ""
|
||||
|
||||
self.userlist.items = 0
|
||||
|
||||
for file in filelist:
|
||||
self.userlist.append_text(file[:-4])
|
||||
self.userlist.items += 1
|
||||
|
||||
if not self.active_user:
|
||||
self.active_user = file[:-4]
|
||||
|
||||
self.userlist.set_active(0)
|
||||
|
||||
self.window.show_all()
|
||||
|
||||
# Start GTK main loop
|
||||
gtk.main()
|
||||
|
||||
def load_model_list(self):
|
||||
"""(Re)load the model list"""
|
||||
|
||||
# Execute the list commond to get the models
|
||||
# status, output = subprocess.getstatusoutput(["howdy list --plain -U " + self.active_user])
|
||||
status = 0
|
||||
output = "1,2020-12-05 14:10:22,sd\n2,2020-12-05 14:22:41,\n3,2020-12-05 14:57:37,Model #3" + self.active_user
|
||||
|
||||
# Create a datamodel
|
||||
self.listmodel = gtk.ListStore(str, str, str)
|
||||
|
||||
# If there was no error
|
||||
if status == 0:
|
||||
# Split the output per line
|
||||
# lines = output.decode("utf-8").split("\n")
|
||||
lines = output.split("\n")
|
||||
|
||||
# Add the models to the datamodel
|
||||
for i in range(len(lines)):
|
||||
self.listmodel.append(lines[i].split(","))
|
||||
|
||||
self.treeview.set_model(self.listmodel)
|
||||
|
||||
def on_about_link(self, label, uri):
|
||||
"""Open links on about page as a non-root user"""
|
||||
try:
|
||||
user = os.getlogin()
|
||||
except Exception:
|
||||
user = os.environ.get("SUDO_USER")
|
||||
|
||||
status, output = subprocess.getstatusoutput(["sudo -u " + user + " timeout 10 xdg-open " + uri])
|
||||
return True
|
||||
|
||||
def exit(self, widget, context):
|
||||
"""Cleanly exit"""
|
||||
if self.capture is not None:
|
||||
self.capture.release()
|
||||
|
||||
gtk.main_quit()
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
# Make sure we quit on a SIGINT
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
# Make sure we run as sudo
|
||||
elevate.elevate()
|
||||
|
||||
# If no models have been created yet or when it is forced, start the onboarding
|
||||
if "--force-onboarding" in sys.argv or not os.path.exists("/lib/security/howdy/models"):
|
||||
import onboarding
|
||||
onboarding.OnboardingWindow()
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
# Class is split so it isn't too long, import split functions
|
||||
import tab_models
|
||||
MainWindow.on_user_add = tab_models.on_user_add
|
||||
MainWindow.on_user_change = tab_models.on_user_change
|
||||
MainWindow.on_model_add = tab_models.on_model_add
|
||||
MainWindow.on_model_delete = tab_models.on_model_delete
|
||||
import tab_video
|
||||
MainWindow.on_page_switch = tab_video.on_page_switch
|
||||
MainWindow.capture_frame = tab_video.capture_frame
|
||||
|
||||
# Open the GTK window
|
||||
window = MainWindow()
|
|
@ -55,7 +55,7 @@ use_cnn = config.getboolean('core', 'use_cnn', fallback=False)
|
|||
|
||||
if use_cnn:
|
||||
face_detector = dlib.cnn_face_detection_model_v1(
|
||||
path + '/../dlib-data/mmod_human_face_detector.dat'
|
||||
path + "/../dlib-data/mmod_human_face_detector.dat"
|
||||
)
|
||||
else:
|
||||
face_detector = dlib.get_frontal_face_detector()
|
||||
|
|
|
@ -17,6 +17,7 @@ import configparser
|
|||
import dlib
|
||||
import cv2
|
||||
import datetime
|
||||
import subprocess
|
||||
import snapshot
|
||||
import numpy as np
|
||||
import _thread as thread
|
||||
|
@ -25,6 +26,18 @@ from recorders.video_capture import VideoCapture
|
|||
from i18n import _
|
||||
|
||||
|
||||
def exit(code):
|
||||
"""Exit while closeing howdy-gtk properly"""
|
||||
global gtk_proc
|
||||
|
||||
# Exit the auth ui process if there is one
|
||||
if "gtk_proc" in globals():
|
||||
gtk_proc.terminate()
|
||||
|
||||
# Exit compare
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
def init_detector(lock):
|
||||
"""Start face detector, encoder and predictor in a new thread"""
|
||||
global face_detector, pose_predictor, face_encoder
|
||||
|
@ -35,7 +48,7 @@ def init_detector(lock):
|
|||
print("\n\tcd " + PATH + "/dlib-data")
|
||||
print("\tsudo ./install.sh\n")
|
||||
lock.release()
|
||||
sys.exit(1)
|
||||
exit(1)
|
||||
|
||||
# Use the CNN detector if enabled
|
||||
if use_cnn:
|
||||
|
@ -64,9 +77,26 @@ def make_snapshot(type):
|
|||
])
|
||||
|
||||
|
||||
def send_to_ui(type, message):
|
||||
"""Send message to the auth ui"""
|
||||
global gtk_proc
|
||||
|
||||
# Only execute of the proccess started
|
||||
if 'gtk_proc' in globals():
|
||||
# Format message so the ui can parse it
|
||||
message = type + "=" + message + " \n"
|
||||
|
||||
# Try to send the message to the auth ui, but it's okay if that fails
|
||||
try:
|
||||
gtk_proc.stdin.write(bytearray(message.encode("ascii")))
|
||||
gtk_proc.stdin.flush()
|
||||
except IOError as err:
|
||||
pass
|
||||
|
||||
|
||||
# Make sure we were given an username to tast against
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit(12)
|
||||
exit(12)
|
||||
|
||||
# Get the absolute path to the current directory
|
||||
PATH = os.path.abspath(__file__ + "/..")
|
||||
|
@ -92,6 +122,15 @@ face_detector = None
|
|||
pose_predictor = None
|
||||
face_encoder = None
|
||||
|
||||
# Start the auth ui
|
||||
try:
|
||||
gtk_proc = subprocess.Popen(["howdy-gtk", "--start-auth-ui"], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
except FileNotFoundError as err:
|
||||
pass
|
||||
|
||||
# Write to the stdin to redraw ui
|
||||
send_to_ui("M", "Starting up...")
|
||||
|
||||
# Try to load the face model from the models folder
|
||||
try:
|
||||
models = json.load(open(PATH + "/models/" + user + ".dat"))
|
||||
|
@ -99,11 +138,11 @@ try:
|
|||
for model in models:
|
||||
encodings += model["data"]
|
||||
except FileNotFoundError:
|
||||
sys.exit(10)
|
||||
exit(10)
|
||||
|
||||
# Check if the file contains a model
|
||||
if len(models) < 1:
|
||||
sys.exit(10)
|
||||
exit(10)
|
||||
|
||||
# Read config from disk
|
||||
config = configparser.ConfigParser()
|
||||
|
@ -158,18 +197,29 @@ timeout = config.getint("video", "timeout")
|
|||
dark_threshold = config.getfloat("video", "dark_threshold")
|
||||
end_report = config.getboolean("debug", "end_report")
|
||||
|
||||
# Initiate histogram equalization
|
||||
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
|
||||
|
||||
# Let the ui know that we're ready
|
||||
send_to_ui("M", "Identifying you...")
|
||||
|
||||
# Start the read loop
|
||||
frames = 0
|
||||
valid_frames = 0
|
||||
timings["fr"] = time.time()
|
||||
dark_running_total = 0
|
||||
|
||||
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
|
||||
|
||||
while True:
|
||||
# Increment the frame count every loop
|
||||
frames += 1
|
||||
|
||||
# Form a string to let the user know we're real busy
|
||||
ui_subtext = "Scanned " + str(valid_frames - dark_tries) + " frames"
|
||||
if (dark_tries > 1):
|
||||
ui_subtext += " (skipped " + str(dark_tries) + " dark frames)"
|
||||
# Show it in the ui as subtext
|
||||
send_to_ui("S", ui_subtext)
|
||||
|
||||
# Stop if we've exceded the time limit
|
||||
if time.time() - timings["fr"] > timeout:
|
||||
# Create a timeout snapshot if enabled
|
||||
|
@ -179,9 +229,9 @@ while True:
|
|||
if dark_tries == valid_frames:
|
||||
print(_("All frames were too dark, please check dark_threshold in config"))
|
||||
print(_("Average darkness: {avg}, Threshold: {threshold}").format(avg=str(dark_running_total / max(1, valid_frames)), threshold=str(dark_threshold)))
|
||||
sys.exit(13)
|
||||
exit(13)
|
||||
else:
|
||||
sys.exit(11)
|
||||
exit(11)
|
||||
|
||||
# Grab a single frame of video
|
||||
frame, gsframe = video_capture.read_frame()
|
||||
|
@ -295,7 +345,7 @@ while True:
|
|||
})
|
||||
|
||||
# End peacefully
|
||||
sys.exit(0)
|
||||
exit(0)
|
||||
|
||||
if exposure != -1:
|
||||
# For a strange reason on some cameras (e.g. Lenoxo X1E)
|
||||
|
|
|
@ -21,5 +21,6 @@ else
|
|||
fi
|
||||
|
||||
# Uncompress the data files and delete the original archive
|
||||
echo " "
|
||||
echo "Unpacking..."
|
||||
bzip2 -d -f *.bz2
|
||||
|
|
Loading…
Reference in a new issue