howdy/howdy-gtk/src/onboarding.py
2021-11-11 10:23:34 +01:00

291 lines
8.5 KiB
Python

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))
for lib_site in ("/lib", "/usr/lib", "/lib64", "/usr/lib64"):
if os.path.exists(lib_site + "/security/howdy/"):
break
else:
lib_site = None
if lib_site is None:
self.downloadoutputlabel.set_text(_("Unable to find Howdy's installation location"))
return
if os.path.exists(lib_site + "/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_site + "/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)
capture = cv2.VideoCapture(device_path)
is_open, frame = capture.read()
if not is_open:
device_rows.append([device_name, device_path, -9, _("No, camera can't be opened")])
continue
try:
if not is_gray(frame):
raise Exception()
except Exception:
device_rows.append([device_name, device_path, -5, _("No, not an infrared camera")])
capture.release()
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.treeview.show()
self.loadinglabel.hide()
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)