Ordnerstruktur sortiert
imports angespasst Stylinganpassungen im Adminbereich (Responsive, Farben für die Feiertagsbuttons)
This commit is contained in:
parent
e71f423c81
commit
9e8fa9ad62
@ -7,10 +7,10 @@ from dateutil.easter import *
|
||||
from nicegui import ui, app, events
|
||||
from nicegui.html import button
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
from lib.users import *
|
||||
from lib.definitions import *
|
||||
from calendar import monthrange
|
||||
from web_ui import *
|
||||
from lib.web_ui import *
|
||||
|
||||
import os.path
|
||||
import os
|
||||
@ -60,7 +60,7 @@ def page_admin():
|
||||
ui.markdown("##Übersichten")
|
||||
|
||||
# Tabelle konstruieren
|
||||
with ui.card():
|
||||
with ui.card().classes('w-full'):
|
||||
|
||||
with ui.row() as timetable_header:
|
||||
year_binder = ValueBinder()
|
||||
@ -131,12 +131,12 @@ def page_admin():
|
||||
@ui.refreshable
|
||||
def timetable():
|
||||
current_user = user(time_user.value)
|
||||
with ui.card() as calendar_card:
|
||||
with ui.card().classes('w-full') as calendar_card:
|
||||
def update_month_and_year():
|
||||
#current_user = user(time_user.value)
|
||||
# Archivstatus
|
||||
days_with_errors = current_user.archiving_validity_check(int(select_year.value), int(select_month.value))
|
||||
with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr') as table_grid:
|
||||
with ui.grid(columns='auto auto auto 1fr 1fr 1fr 1fr').classes('w-full md:min-w-[600px] lg:min-w-[800px]') as table_grid:
|
||||
if int(select_month.value) > 1:
|
||||
archive_status = current_user.get_archive_status(int(select_year.value),
|
||||
int(select_month.value))
|
||||
@ -894,7 +894,7 @@ Dies kann nicht rückgängig gemacht werden!''')
|
||||
with ui.row():
|
||||
for entry in year_dict[year_entry]:
|
||||
date_label = entry.strftime("%d.%m.")
|
||||
ui.button(f"{data['holidays'][entry.strftime('%Y-%m-%d')]} ({date_label})", on_click=lambda entry=entry: del_holiday_entry(entry)).classes('text-sm')
|
||||
ui.button(f"{data['holidays'][entry.strftime('%Y-%m-%d')]} ({date_label})", color='cyan-300', on_click=lambda entry=entry: del_holiday_entry(entry)).classes('text-sm')
|
||||
holiday_buttons_grid()
|
||||
|
||||
holiday_section()
|
@ -4,10 +4,9 @@ from logging import exception
|
||||
|
||||
from nicegui import *
|
||||
|
||||
import ui
|
||||
from definitions import *
|
||||
from web_ui import *
|
||||
from users import *
|
||||
from lib.definitions import *
|
||||
from lib.web_ui import *
|
||||
from lib.users import *
|
||||
from datetime import datetime
|
||||
|
||||
import calendar
|
@ -2,12 +2,13 @@
|
||||
# Quasi-Konstanten
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
app_title = "Zeiterfassung"
|
||||
app_version = ("0.0.0")
|
||||
|
||||
# Standardpfade
|
||||
scriptpath = os.path.dirname(os.path.abspath(__file__))
|
||||
scriptpath = str(Path(os.path.dirname(os.path.abspath(__file__))).parent.absolute())
|
||||
userfolder = "users"
|
||||
|
||||
# Dateinamen
|
@ -5,15 +5,15 @@ from nicegui import ui, app, Client
|
||||
from nicegui.page import page
|
||||
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
from lib.users import *
|
||||
from lib.definitions import *
|
||||
from calendar import monthrange, month_name
|
||||
|
||||
import hashlib
|
||||
import calendar
|
||||
import locale
|
||||
|
||||
from web_ui import *
|
||||
from lib.web_ui import *
|
||||
|
||||
@ui.page('/')
|
||||
def homepage():
|
||||
@ -24,7 +24,7 @@ def homepage():
|
||||
current_user = user(app.storage.user["active_user"])
|
||||
except:
|
||||
del(app.storage.user["active_user"])
|
||||
ui.navigate.to('/')
|
||||
ui.navigate.reload()
|
||||
pageheader(f"Willkommen, {current_user.fullname}")
|
||||
|
||||
today = datetime.datetime.now()
|
@ -1,10 +1,10 @@
|
||||
from datetime import datetime
|
||||
|
||||
from nicegui import ui, app
|
||||
from web_ui import *
|
||||
from lib.web_ui import *
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
from lib.users import *
|
||||
from lib.definitions import *
|
||||
from calendar import monthrange
|
||||
|
||||
import hashlib
|
13
lib/settings.json
Normal file
13
lib/settings.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"admin_user": "admin",
|
||||
"admin_password": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
|
||||
"port": "8090",
|
||||
"secret": "ftgzuhjikg,mt5jn46uzer8sfi9okrmtzjhndfierko5zltjhdgise",
|
||||
"times_on_touchscreen": true,
|
||||
"photos_on_touchscreen": true,
|
||||
"touchscreen": true,
|
||||
"picure_height": 200,
|
||||
"button_height": 300,
|
||||
"user_notes": true,
|
||||
"holidays": {}
|
||||
}
|
@ -2,9 +2,9 @@ from datetime import datetime
|
||||
|
||||
from nicegui import ui, app
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
from web_ui import *
|
||||
from lib.users import *
|
||||
from lib.definitions import *
|
||||
from lib.web_ui import *
|
||||
from calendar import monthrange
|
||||
|
||||
import hashlib
|
@ -11,7 +11,7 @@ import json
|
||||
import shutil
|
||||
import re
|
||||
|
||||
from definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, standard_adminsettings, standard_usersettings
|
||||
from lib.definitions import userfolder, scriptpath, usersettingsfilename, photofilename, status_in, status_out, standard_adminsettings, standard_usersettings
|
||||
|
||||
# Benutzerklasse
|
||||
|
@ -2,8 +2,8 @@ from datetime import datetime
|
||||
|
||||
from nicegui import ui, app
|
||||
|
||||
from users import *
|
||||
from definitions import *
|
||||
from lib.users import *
|
||||
from lib.definitions import *
|
||||
from calendar import monthrange
|
||||
|
||||
import hashlib
|
22
qr_scanner_example.py
Normal file
22
qr_scanner_example.py
Normal file
@ -0,0 +1,22 @@
|
||||
import cv2
|
||||
import webbrowser
|
||||
|
||||
cap = cv2.VideoCapture(0)
|
||||
# initialize the cv2 QRCode detector
|
||||
detector = cv2.QRCodeDetector()
|
||||
|
||||
while True:
|
||||
_, img = cap.read()
|
||||
# detect and decode
|
||||
data, bbox, _ = detector.detectAndDecode(img)
|
||||
# check if there is a QRCode in the image
|
||||
if data:
|
||||
a = data
|
||||
break
|
||||
cv2.imshow("QRCODEscanner", img)
|
||||
if cv2.waitKey(1) == ord("q"):
|
||||
break
|
||||
|
||||
b = webbrowser.open(str(a))
|
||||
cap.release()
|
||||
cv2.destroyAllWindows()
|
BIN
sounds/3beeps.mp3
Normal file
BIN
sounds/3beeps.mp3
Normal file
Binary file not shown.
BIN
sounds/beep.mp3
Normal file
BIN
sounds/beep.mp3
Normal file
Binary file not shown.
BIN
sounds/power-on.mp3
Normal file
BIN
sounds/power-on.mp3
Normal file
Binary file not shown.
BIN
sounds/store_beep.mp3
Normal file
BIN
sounds/store_beep.mp3
Normal file
Binary file not shown.
BIN
sounds/success.mp3
Normal file
BIN
sounds/success.mp3
Normal file
Binary file not shown.
BIN
sounds/ui-off.mp3
Normal file
BIN
sounds/ui-off.mp3
Normal file
Binary file not shown.
BIN
sounds/ui-on.mp3
Normal file
BIN
sounds/ui-on.mp3
Normal file
Binary file not shown.
BIN
sounds/ui-sound.mp3
Normal file
BIN
sounds/ui-sound.mp3
Normal file
Binary file not shown.
217
ui.py
217
ui.py
@ -1,217 +0,0 @@
|
||||
# Zeiterfassung
|
||||
# UI Definitionen
|
||||
|
||||
import tkinter as tk
|
||||
import locale
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
|
||||
from time import strftime
|
||||
from definitions import app_title, app_version, status_in
|
||||
from users import user as uo
|
||||
from users import list_users
|
||||
|
||||
# Pinpad
|
||||
|
||||
class win_pinpad(tk.Toplevel):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
def update_time():
|
||||
string_time = strftime('%A, der %d.%m.%Y - %H:%M:%S')
|
||||
nonlocal digital_clock
|
||||
digital_clock.config(text=string_time)
|
||||
digital_clock.after(1000, update_time)
|
||||
|
||||
self.title(app_title + " " + app_version)
|
||||
|
||||
# Digital clock label configuration
|
||||
digital_clock = tk.Label(self)
|
||||
digital_clock.grid(row=0, column=0, columnspan=3, padx=10, pady=10)
|
||||
# Initial call to update_time function
|
||||
update_time()
|
||||
|
||||
# Benutzernummer
|
||||
def usernr_changed(UserNr):
|
||||
nonlocal usernr
|
||||
if len(str(usernr.get())) > 0:
|
||||
buttons["OK"].configure(state="active")
|
||||
else:
|
||||
buttons["OK"].configure(state="disabled")
|
||||
|
||||
|
||||
tk.Label(self, text="Benutzernummer:").grid(row=1, column=0)
|
||||
UserNr = tk.StringVar()
|
||||
UserNr.trace("w", lambda name, index, mode, UserNr=UserNr: usernr_changed(UserNr))
|
||||
usernr = tk.Entry(self, width=10, textvariable=UserNr)
|
||||
usernr.grid(row=1,column=1)
|
||||
|
||||
# Pinpad
|
||||
|
||||
def buttonPress(key):
|
||||
|
||||
nonlocal usernr
|
||||
if type(key) is int:
|
||||
if key < 10:
|
||||
usernr.insert('end', str(key))
|
||||
if key =="OK":
|
||||
print("OK pressed")
|
||||
if key == "<-":
|
||||
usernr.delete(usernr.index("end") - 1 )
|
||||
if len(usernr.get()) > 0:
|
||||
buttons["OK"].configure(state="active")
|
||||
else:
|
||||
buttons["OK"].configure(state="disabled")
|
||||
|
||||
# Buttons definieren
|
||||
button_width = 7
|
||||
button_height = 3
|
||||
pinframe = tk.Frame(self)
|
||||
pinframe.grid(row=2, column=0, columnspan=3, padx=10, pady=10)
|
||||
buttons = { }
|
||||
|
||||
keys = [
|
||||
[ 1, 2, 3],
|
||||
[ 4, 5, 6],
|
||||
[ 7, 8, 9],
|
||||
[ "<-", 0, "OK"]
|
||||
]
|
||||
|
||||
for y, row in enumerate(keys, 1):
|
||||
for x, key in enumerate(row):
|
||||
button = tk.Button(pinframe, width=button_width, height=button_height, text=key, command=lambda key=key: buttonPress(key))
|
||||
button.grid(row=y, column=x)
|
||||
buttons[key] = button
|
||||
|
||||
buttons["OK"].configure(state="disabled")
|
||||
|
||||
usernr.focus_set()
|
||||
|
||||
class win_userlist(tk.Toplevel):
|
||||
def __init__(self, parent):
|
||||
super().__init__(parent)
|
||||
|
||||
def update_time():
|
||||
string_time = strftime('%A, der %d.%m.%Y - %H:%M:%S')
|
||||
nonlocal digital_clock
|
||||
digital_clock.config(text=string_time)
|
||||
digital_clock.after(1000, update_time)
|
||||
|
||||
self.title(app_title + " " + app_version)
|
||||
|
||||
# Digital clock label configuration
|
||||
digital_clock = tk.Label(self)
|
||||
digital_clock.grid(row=0, column=0, columnspan=3, padx=10, pady=10)
|
||||
# Initial call to update_time function
|
||||
update_time()
|
||||
|
||||
tk.Label(self, text="Benutzer auswählen").grid(row=1, column=0, columnspan=2)
|
||||
|
||||
# Button Frame
|
||||
button_frame = tk.Frame(self)
|
||||
button_frame.grid(row=2, column=0, columnspan=2, padx=0, pady=10)
|
||||
userlist = list_users()
|
||||
|
||||
|
||||
# Button Dictionary
|
||||
buttons = { }
|
||||
button_row_index = 0
|
||||
|
||||
for name in userlist:
|
||||
button = tk.Button(button_frame, text=name)
|
||||
button.grid(row=button_row_index, column=0, pady=5, sticky="ew")
|
||||
buttons[name] = button
|
||||
button_row_index = button_row_index + 1
|
||||
|
||||
class win_stamping(tk.Toplevel):
|
||||
def __init__(self, parent, user):
|
||||
super().__init__(parent)
|
||||
def update_time():
|
||||
string_time = strftime('%A, der %d.%m.%Y - %H:%M:%S')
|
||||
nonlocal digital_clock
|
||||
digital_clock.config(text=string_time)
|
||||
digital_clock.after(1000, update_time)
|
||||
|
||||
self.title(app_title + " " + app_version)
|
||||
|
||||
# Benutzer feststellen
|
||||
|
||||
current_user = uo(user)
|
||||
|
||||
# Digital clock label configuration
|
||||
digital_clock = tk.Label(self)
|
||||
digital_clock.grid(row=0, column=0, columnspan=3, padx=10, pady=10)
|
||||
# Initial call to update_time function
|
||||
update_time()
|
||||
|
||||
# Benutzer anzeigen
|
||||
tk.Label(self, text=current_user.fullname).grid(row=1, column=0, pady=10, columnspan=3)
|
||||
|
||||
todays_hours = tk.Label(self, text="Arbeitsstunden erscheinen hier")
|
||||
todays_hours.grid(row=2, column=0, pady=10, columnspan=3)
|
||||
|
||||
in_button = tk.Button(self, text="Einstempeln", bg="green")
|
||||
out_button = tk.Button(self, text="Ausstempeln", bg="red")
|
||||
|
||||
if current_user.stamp_status() == status_in:
|
||||
in_button.configure(state="disabled")
|
||||
out_button.configure(state="active")
|
||||
out_button.focus_set()
|
||||
else:
|
||||
in_button.configure(state="active")
|
||||
out_button.configure(state="disabled")
|
||||
in_button.focus_set()
|
||||
in_button.grid(row=3, column = 0)
|
||||
out_button.grid(row=3, column=2)
|
||||
|
||||
button_frame = tk.Frame(self, relief="groove")
|
||||
button_frame.grid(row=4, column=0, columnspan=3, pady=10)
|
||||
|
||||
overview_workinghours = tk.Button(button_frame, text="Übersicht Arbeitszeiten")
|
||||
overview_missed = tk.Button(button_frame, text="Übersicht Fehlzeiten")
|
||||
overview_data = tk.Button(button_frame, text="Stammdaten")
|
||||
|
||||
overview_workinghours.grid(row=0, column=0, sticky="ew")
|
||||
overview_missed.grid(row=1, column=0, sticky="ew")
|
||||
overview_data.grid(row=2, column=0, sticky="ew")
|
||||
|
||||
button_close = tk.Button(self, text="Schließen")
|
||||
button_close.grid(row=5, column=1)
|
||||
|
||||
#========================================================
|
||||
|
||||
class mainwindow(tk.Tk):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.geometry('300x200')
|
||||
self.title('Main Window')
|
||||
|
||||
# place a button on the root window
|
||||
tk.Button(self,
|
||||
text='PinPad Window',
|
||||
command=self.open_pinpad).pack(expand=True)
|
||||
tk.Button(self,
|
||||
text='Userlist Window',
|
||||
command=self.open_userlist).pack(expand=True)
|
||||
tk.Button(self,
|
||||
text='Stamping Window',
|
||||
command=self.open_stamping).pack(expand=True)
|
||||
|
||||
def open_pinpad(self):
|
||||
window = win_pinpad(self)
|
||||
window.grab_set()
|
||||
|
||||
def open_userlist(self):
|
||||
window = win_userlist(self)
|
||||
window.grab_set()
|
||||
|
||||
def open_stamping(self):
|
||||
window = win_stamping(self, user="testuser")
|
||||
window.grab_set()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = mainwindow()
|
||||
app.mainloop()
|
||||
|
||||
|
4
users/filler2/2025-5.json
Normal file
4
users/filler2/2025-5.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"archived": 0,
|
||||
"total_hours": 0
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
1747642816
|
||||
1747642898
|
||||
1747642972
|
||||
1747642976
|
||||
1747643508
|
||||
1747643521
|
||||
1747643564
|
||||
1747643566
|
||||
1747643603
|
||||
1747644615
|
@ -1,8 +1,7 @@
|
||||
{
|
||||
"username": "filler2",
|
||||
"fullname": "filler2",
|
||||
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||
"api_key": "0f36286bf8c96de1922ab41e2682ba5a81793525",
|
||||
"password": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
||||
"workhours": {
|
||||
"2025-05-16": {
|
||||
"1": 0,
|
||||
@ -14,5 +13,6 @@
|
||||
"7": 0,
|
||||
"vacation": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"api_key": "43ec918e7d773cb23ab3113d18059a83fee389ac"
|
||||
}
|
@ -2,17 +2,17 @@
|
||||
"username": "filler3",
|
||||
"fullname": "filler3",
|
||||
"password": "37a8eec1ce19687d132fe29051dca629d164e2c4958ba141d5f4133a33f0688f",
|
||||
"api_key": "9e3f37809cd898a3db340c453df53bd0793a99fa",
|
||||
"workhours": {
|
||||
"2025-05-16": {
|
||||
"1": 0,
|
||||
"2": 0,
|
||||
"3": 0,
|
||||
"4": 0,
|
||||
"5": 0,
|
||||
"1": "6",
|
||||
"2": "6",
|
||||
"3": "6",
|
||||
"4": "6",
|
||||
"5": "6",
|
||||
"6": 0,
|
||||
"7": 0,
|
||||
"vacation": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
"api_key": "9e3f37809cd898a3db340c453df53bd0793a99fa"
|
||||
}
|
@ -11,22 +11,13 @@
|
||||
"13": "U"
|
||||
},
|
||||
"notes": {
|
||||
"5": {
|
||||
"user": "Jo, das ging echt ab.",
|
||||
"admin": "Streik\n\nUnd anderes"
|
||||
},
|
||||
"4": {
|
||||
"admin": "Testeintrag\n\nZusatzeintrag"
|
||||
},
|
||||
"5": {},
|
||||
"4": {},
|
||||
"2": {},
|
||||
"1": {},
|
||||
"9": {
|
||||
"user": "Dieses ist ein Testeintrag.",
|
||||
"admin": "Das sollte der Testuser nicht sehen"
|
||||
},
|
||||
"12": {
|
||||
"user": "Testtext"
|
||||
},
|
||||
"14": {}
|
||||
"9": {},
|
||||
"12": {},
|
||||
"14": {},
|
||||
"22": {}
|
||||
}
|
||||
}
|
113
webcam_example.py
Normal file
113
webcam_example.py
Normal file
@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env python3
|
||||
import base64
|
||||
import signal
|
||||
import time
|
||||
import webbrowser
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from fastapi import Response
|
||||
|
||||
from nicegui import Client, app, core, run, ui
|
||||
|
||||
# In case you don't have a webcam, this will provide a black placeholder image.
|
||||
black_1px = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAA1JREFUGFdjYGBg+A8AAQQBAHAgZQsAAAAASUVORK5CYII='
|
||||
placeholder = Response(content=base64.b64decode(black_1px.encode('ascii')), media_type='image/png')
|
||||
|
||||
|
||||
def convert(frame: np.ndarray) -> bytes:
|
||||
"""Converts a frame from OpenCV to a JPEG image.
|
||||
|
||||
This is a free function (not in a class or inner-function),
|
||||
to allow run.cpu_bound to pickle it and send it to a separate process.
|
||||
"""
|
||||
_, imencode_image = cv2.imencode('.jpg', frame)
|
||||
return imencode_image.tobytes()
|
||||
|
||||
|
||||
def setup() -> None:
|
||||
# OpenCV is used to access the webcam.
|
||||
video_capture = cv2.VideoCapture(0)
|
||||
detector = cv2.QRCodeDetector()
|
||||
|
||||
blocker = False
|
||||
blockset = 0
|
||||
|
||||
@app.get('/video/frame')
|
||||
# Thanks to FastAPI's `app.get` it is easy to create a web route which always provides the latest image from OpenCV.
|
||||
async def grab_video_frame() -> Response:
|
||||
nonlocal blocker
|
||||
if time.time() - blockset > 5:
|
||||
blocker = False
|
||||
|
||||
if not video_capture.isOpened():
|
||||
return placeholder
|
||||
# The `video_capture.read` call is a blocking function.
|
||||
# So we run it in a separate thread (default executor) to avoid blocking the event loop.
|
||||
_, frame = await run.io_bound(video_capture.read)
|
||||
if frame is None:
|
||||
return placeholder
|
||||
# `convert` is a CPU-intensive function, so we run it in a separate process to avoid blocking the event loop and GIL.
|
||||
jpeg = await run.cpu_bound(convert, frame)
|
||||
|
||||
# QR-Handling
|
||||
|
||||
def function_call():
|
||||
b = webbrowser.open(str(a))
|
||||
print('\a')
|
||||
nonlocal blocker
|
||||
nonlocal blockset
|
||||
blocker = True
|
||||
blockset = time.time()
|
||||
|
||||
if not blocker:
|
||||
_, img = video_capture.read()
|
||||
# detect and decode
|
||||
data, bbox, _ = detector.detectAndDecode(img)
|
||||
# check if there is a QRCode in the image
|
||||
if data:
|
||||
a = data
|
||||
function_call()
|
||||
# cv2.imshow("QRCODEscanner", img)
|
||||
if cv2.waitKey(1) == ord("q"):
|
||||
function_call()
|
||||
|
||||
return Response(content=jpeg, media_type='image/jpeg')
|
||||
|
||||
# For non-flickering image updates and automatic bandwidth adaptation an interactive image is much better than `ui.image()`.
|
||||
video_image = ui.interactive_image().classes('w-full h-full')
|
||||
# A timer constantly updates the source of the image.
|
||||
# Because data from same paths is cached by the browser,
|
||||
# we must force an update by adding the current timestamp to the source.
|
||||
|
||||
ui.timer(interval=0.1, callback=lambda: video_image.set_source(f'/video/frame?{time.time()}'))
|
||||
|
||||
async def disconnect() -> None:
|
||||
"""Disconnect all clients from current running server."""
|
||||
for client_id in Client.instances:
|
||||
await core.sio.disconnect(client_id)
|
||||
|
||||
def handle_sigint(signum, frame) -> None:
|
||||
# `disconnect` is async, so it must be called from the event loop; we use `ui.timer` to do so.
|
||||
ui.timer(0.1, disconnect, once=True)
|
||||
# Delay the default handler to allow the disconnect to complete.
|
||||
ui.timer(1, lambda: signal.default_int_handler(signum, frame), once=True)
|
||||
|
||||
async def cleanup() -> None:
|
||||
# This prevents ugly stack traces when auto-reloading on code change,
|
||||
# because otherwise disconnected clients try to reconnect to the newly started server.
|
||||
await disconnect()
|
||||
# Release the webcam hardware so it can be used by other applications again.
|
||||
video_capture.release()
|
||||
|
||||
app.on_shutdown(cleanup)
|
||||
# We also need to disconnect clients when the app is stopped with Ctrl+C,
|
||||
# because otherwise they will keep requesting images which lead to unfinished subprocesses blocking the shutdown.
|
||||
signal.signal(signal.SIGINT, handle_sigint)
|
||||
|
||||
|
||||
# All the setup is only done when the server starts. This avoids the webcam being accessed
|
||||
# by the auto-reload main process (see https://github.com/zauberzeug/nicegui/discussions/2321).
|
||||
app.on_startup(setup)
|
||||
|
||||
ui.run(port=9005)
|
@ -1,19 +1,19 @@
|
||||
#!/usr/bin/env python3
|
||||
# Zeiterfassung
|
||||
|
||||
from web_ui import *
|
||||
from admin import *
|
||||
from login import *
|
||||
from users import *
|
||||
from touchscreen import *
|
||||
from definitions import *
|
||||
from api import *
|
||||
from homepage import *
|
||||
from lib.web_ui import *
|
||||
from lib.admin import *
|
||||
from lib.login import *
|
||||
from lib.users import *
|
||||
from lib.touchscreen import *
|
||||
from lib.definitions import *
|
||||
from lib.api import *
|
||||
from lib.homepage import *
|
||||
|
||||
import json
|
||||
import argparse
|
||||
|
||||
from web_ui import hash_password
|
||||
from lib.web_ui import hash_password
|
||||
|
||||
|
||||
class Commandline_Header:
|
Loading…
x
Reference in New Issue
Block a user