mirror of
https://github.com/boltgolt/howdy.git
synced 2024-09-19 09:51:19 +02:00
First working implementation of auth ui in gtk
This commit is contained in:
parent
3c9537a35f
commit
d154c56566
3 changed files with 174 additions and 9 deletions
125
howdy-gtk/authsticky.py
Normal file
125
howdy-gtk/authsticky.py
Normal file
|
@ -0,0 +1,125 @@
|
|||
# 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
|
||||
|
||||
message = "Starting up... "
|
||||
subtext = ""
|
||||
|
||||
|
||||
class StickyWindow(gtk.Window):
|
||||
def __init__(self):
|
||||
gtk.Window.__init__(self)
|
||||
|
||||
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)
|
||||
|
||||
# Draw
|
||||
self.connect("draw", self.draw)
|
||||
self.connect("destroy", self.exit)
|
||||
self.connect("button-press-event", self.exit)
|
||||
|
||||
darea = gtk.DrawingArea()
|
||||
darea.set_size_request(windowWidth, windowHeight)
|
||||
self.add(darea)
|
||||
|
||||
screen = self.get_screen()
|
||||
visual = screen.get_rgba_visual()
|
||||
if visual and screen.is_composited():
|
||||
self.set_visual(visual)
|
||||
|
||||
# TODO: handle more than 1 screen
|
||||
self.move((screen.get_width() / 2) - (windowWidth / 2), 0)
|
||||
|
||||
self.show_all()
|
||||
self.resize(windowWidth, windowHeight)
|
||||
|
||||
gobject.timeout_add(100, self.test)
|
||||
|
||||
gtk.main()
|
||||
|
||||
def draw(self, widget, ctx):
|
||||
# Change cursor to the kill icon
|
||||
self.get_window().set_cursor(gdk.Cursor(gdk.CursorType.PIRATE))
|
||||
|
||||
ctx.set_source_rgba(0, 0, 0, .9)
|
||||
ctx.set_operator(cairo.OPERATOR_SOURCE)
|
||||
ctx.paint()
|
||||
ctx.set_operator(cairo.OPERATOR_OVER)
|
||||
|
||||
dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
image_surface = cairo.ImageSurface.create_from_png(dir + "/logo.png")
|
||||
ratio = float(windowHeight - 20) / float(image_surface.get_height())
|
||||
|
||||
ctx.translate(15, 10)
|
||||
ctx.scale(ratio, ratio)
|
||||
ctx.set_source_surface(image_surface)
|
||||
ctx.paint()
|
||||
|
||||
ctx.set_source_rgba(255, 255, 255, .9)
|
||||
ctx.set_font_size(80)
|
||||
|
||||
if subtext:
|
||||
ctx.move_to(380, 145)
|
||||
else:
|
||||
ctx.move_to(380, 170)
|
||||
|
||||
ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
||||
ctx.show_text(message)
|
||||
|
||||
ctx.set_source_rgba(230, 230, 230, .8)
|
||||
ctx.set_font_size(40)
|
||||
ctx.move_to(380, 210)
|
||||
ctx.select_font_face("Arial", cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
|
||||
ctx.show_text(subtext)
|
||||
|
||||
def exit(self, widget, context):
|
||||
gtk.main_quit()
|
||||
|
||||
def test(self):
|
||||
global message, subtext
|
||||
|
||||
comm = sys.stdin.readline()[:-1]
|
||||
|
||||
if comm:
|
||||
if comm[0] == "M":
|
||||
message = comm[2:]
|
||||
if comm[0] == "S":
|
||||
subtext = comm[2:]
|
||||
|
||||
self.queue_draw()
|
||||
gobject.timeout_add(100, self.test)
|
||||
|
||||
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
|
||||
window = StickyWindow()
|
BIN
howdy-gtk/logo.png
Normal file
BIN
howdy-gtk/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
|
@ -17,12 +17,21 @@ import configparser
|
|||
import dlib
|
||||
import cv2
|
||||
import datetime
|
||||
import subprocess
|
||||
import snapshot
|
||||
import numpy as np
|
||||
import _thread as thread
|
||||
from recorders.video_capture import VideoCapture
|
||||
|
||||
|
||||
def exit(code):
|
||||
"""Exit while closeing howdy-gtk properly"""
|
||||
global gtk_proc
|
||||
|
||||
gtk_proc.terminate()
|
||||
sys.exit(code)
|
||||
|
||||
|
||||
def init_detector(lock):
|
||||
"""Start face detector, encoder and predictor in a new thread"""
|
||||
global face_detector, pose_predictor, face_encoder
|
||||
|
@ -33,7 +42,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:
|
||||
|
@ -62,9 +71,24 @@ def make_snapshot(type):
|
|||
])
|
||||
|
||||
|
||||
def send_to_ui(type, message):
|
||||
"""Send message to the auth ui"""
|
||||
global gtk_proc
|
||||
|
||||
# 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__ + "/..")
|
||||
|
@ -90,6 +114,11 @@ face_detector = None
|
|||
pose_predictor = None
|
||||
face_encoder = None
|
||||
|
||||
# Start the auth ui
|
||||
gtk_proc = subprocess.Popen(["python3", "-u", "../howdy-gtk/authsticky.py"], stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||
# 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"))
|
||||
|
@ -97,11 +126,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()
|
||||
|
@ -156,18 +185,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) + " 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
|
||||
|
@ -177,9 +217,9 @@ while True:
|
|||
if dark_tries == valid_frames:
|
||||
print("All frames were too dark, please check dark_threshold in config")
|
||||
print("Average darkness: " + 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()
|
||||
|
@ -283,7 +323,7 @@ while True:
|
|||
make_snapshot("SUCCESSFUL")
|
||||
|
||||
# End peacefully
|
||||
sys.exit(0)
|
||||
exit(0)
|
||||
|
||||
if exposure != -1:
|
||||
# For a strange reason on some cameras (e.g. Lenoxo X1E)
|
||||
|
|
Loading…
Reference in a new issue