videocapture.settings - Videocapture
Table of Contents
1 Namespace videocapture.settings
2 Settings format
2.1 settings.edn
2.1.1 Overview
Name | Description | Generate? | |
---|---|---|---|
:settings/smb | Access and location data for data storage on an SMB server. | ||
:settings/form | Data to pre-configure the GUI's input fields. | ||
:settings/gui | Information about which frontend to use. | ||
:settings/configuration | The config.edn configuration to be used. |
||
:settings/camera | The camera configuration. | ||
:settings/supervisor | Configuration settings that describe/configure the supervisor. |
2.1.2 SMB Settings
Key: :smb
Name | Format | Description | Generate? |
---|---|---|---|
:settings-smb/username | string? | The SMB server username | x |
:settings-smb/password | string? | The SMB server password | x |
:settings-smb/dns | string? | The SMB server address | x |
:settings-smb/directory | string? | The directory on the server | x |
2.1.3 Configuration
Key: :configuration
(s/def :settings/configuration keyword?)
2.1.4 Form
Key: :form
Name | Format | Description | Generate? | Optional |
---|---|---|---|---|
:settings-form/room | string? | x | ||
:settings-form/groups | (s/coll-of string?) | x | x |
2.1.5 GUI
Key: :gui
(s/def :settings/gui string?)
2.1.6 Camera
Key: :camera
V4L2 Camera
Name | Format | Description | Generate? |
---|---|---|---|
:settings-camera-device/address | string? | The file name of the device, e.g. /dev/video0 . |
x |
RTSP Stream
Name | Format | Description | Generate? |
---|---|---|---|
:settings-camera-rtsp/address | string? | x | |
:settings-camera-rtsp/username | string? | x | |
:settings-camera-rtsp/password | string? | x |
(s/def :settings-camera/type #{:device :rtsp}) <<type-from-table(table=settings-camera-v4l2-type)>> <<type-from-table(table=settings-camera-rtsp-type)>> (defmulti camera-type :type) (defmethod camera-type :device [_] (s/keys :req-un [:settings-camera-device/address])) (defmethod camera-type :rtsp [_] (s/keys :req-un [:settings-camera-rtsp/address :settings-camera-rtsp/username :settings-camera-rtsp/password])) (s/def :settings/camera (s/coll-of (s/multi-spec camera-type :settings-camera/type)))
2.1.7 Supervisor
Key: :supervisor
Name | Format | Description | Generate? |
---|---|---|---|
:settings-supervisor/ip | string? | A valid IPv4 addressing the supervisor. | x |
:settings-supervisor/port | int? | The network port to be used. | x |
:settings-supervisor/activated | boolean? | Value true means: this instance is the supervisor. |
x |
2.1.8 An example settings.edn
{:smb {:username "wisc" :password "secret" :dns "wiscserver.unibe.ch" :directory "videoupload"} :configuration :wisc :form {:room "A001" :groups ["A" "B" "C" "D" "E" "F" "G" "H"]} :gui "wisc-mainwindow.fxml" :camera [{:type :device :address "/dev/video0"}] :supervisor {:ip "" :port 9898 :activated false}}
3 Settingskeeping
(defn get-config-key [settings] {:post [(keyword? %)]} (get-in @settings [:configuration])) (defn get-setting "Fetches a setting from the combined settings structure. The keyword `:C' gets replaced by the value of the `:configuration' variable in settings.edn." [top & levels] {:pre [(keyword? top)]} (let [fst (if (= top :C) [:configs (get-config-key (:settings @settings))] [top]) {:keys [settings config]} @settings result (or (get-in @settings (into fst levels)) (get-in @config (into fst levels)))] (trace fst levels " => " result) result)) (defn update-setting "Replaces a setting with a temporary value." [keys value] (debug keys " => " value) (swap! (:settings @settings) update-in keys (fn [_] value))) (defn init-settings [] (let [confdir (if (str/blank? (System/getenv "XDG_CONFIG_HOME")) "" (str (System/getenv "XDG_CONFIG_HOME") "/")) settings (atom (s/conform ::settings (read-string (Files/readString (.toPath (io/file (str confdir "settings.edn"))))))) config (read-string (Files/readString (.toPath (io/file (str confdir "config.edn")))))] (swap! settings assoc :xdg {:config confdir :home (str (System/getenv "HOME") "/videocapture/")}) (debug "Checking settings validity:" (s/valid? ::settings @settings)) (debug "Checking settings validity, smb:" (s/valid? :settings/smb (:smb @settings))) (debug "Checking settings validity, configuration:" (s/valid? :settings/configuration (:configuration @settings))) (debug "Checking settings validity, form:" (s/valid? :settings/form (:form @settings))) (debug "Checking settings validity, gui:" (s/valid? :settings/gui (:gui @settings))) (debug "Checking settings validity, camera:" (s/valid? :settings/camera (:camera @settings))) (debug "Checking settings validity, supervisor:" (s/valid? :settings/supervisor (:supervisor @settings))) (when (= @settings :clojure.spec.alpha/invalid) (fatal "The settings file does not conform to the spec; terminating.") (gui-util/alert :error "Inkorrekte Einstellungsdatei" "Die Einstellungsdatei \"settings.edn\" entspricht nicht der Spezifikation. Details entnehmen Sie der Logdatei. Das Programm wird jetzt beendet.") (mount/stop) (System/exit 0)) {:settings settings :config (atom config)})) (defn destruct-settings [settings] nil) (mount/defstate settings :start (init-settings) :stop (destruct-settings settings))
4 Settings window
4.1 Initialization
(defn init-config-list [instance] (let [config-box ^ChoiceBox (.-configBox instance) config-choices (keys (get-setting :configs))] (debug config-choices) (.addAll (.getItems config-box) (into-array String (map name config-choices))) (.setValue config-box (name (get-setting :configuration))))) (defn init-group-list [instance] (.setText (.-roomNumber instance) (get-setting :form :room)) (let [group-list ^ListView (.-groupList instance)] (.setSelectionMode (.getSelectionModel group-list) SelectionMode/SINGLE) (.addAll (.getItems group-list) (into-array String (get-setting :form :groups))) (.setOnMouseClicked group-list (fx/fi javafx.event.EventHandler [event] (.setText ^TextField (.-groupInput instance) (-> group-list .getSelectionModel .getSelectedItem)))))) (defn init-camera-pane [instance] (.setText (.-camType instance) (name (get-setting :camera :type))) (.setText (.-camAddress instance) (get-setting :camera :address)) (.setText (.-camUsername instance) (get-setting :camera :username)) (.setText (.-camPassword instance) (get-setting :camera :password))) (defn init-server-pane [instance] (.setText (.-smbServer instance) (get-setting :smb :dns)) (.setText (.-smbDir instance) (get-setting :smb :directory)) (.setText (.-smbUser instance) (get-setting :smb :username)) (.setText (.-smbPassword instance) (get-setting :smb :password))) (defn init-supervisor-pane [instance] (if (get-setting :clients :activated) (do (.setText (.-supervisorState instance) "Aktiviert") (.setSelected (.-supervisorState instance) true)) (do (.setText (.-supervisorState instance) "Deaktiviert") (.setSelected (.-supervisorState instance) false))) (.setOnAction (.-supervisorState instance) (fx/fi javafx.event.EventHandler [event] (if (= (.getText (.-supervisorState instance)) "Aktiviert") (.setText (.-supervisorState instance) "Deaktiviert") (.setText (.-supervisorState instance) "Aktiviert")))) (let [source-list ^ListView (.-sourceList instance)] (.setSelectionMode (.getSelectionModel source-list) SelectionMode/SINGLE) (.addAll (.getItems source-list) (into-array String (get-setting :supervisor :videosources))) (.setOnMouseClicked source-list (fx/fi javafx.event.EventHandler [event] (.setText ^TextField (.-sourceInput instance) (-> source-list .getSelectionModel .getSelectedItem)))))) (defn stage-init [instance] (init-config-list instance) (init-group-list instance) (init-camera-pane instance) (init-supervisor-pane instance) (init-server-pane instance) ) (defn init "This initializes the settings window." [^Window parent] (let [stage (new Stage) content (fxml/load-fxml-with-controller (io/resource "fxml/settings.fxml") "videocapture.settings/stage-init") scene (new javafx.scene.Scene content 1024 768)] (.add (.getStylesheets scene) (.toExternalForm (io/resource "css/gui.css"))) (.initOwner stage parent) (.initModality stage Modality/WINDOW_MODAL) (.setTitle stage "Einstellungen") (.setScene stage scene) (.show stage) stage))
4.2 Manipulation
(defn move-listview-item [listview direction] (let [items (.getItems listview) index (-> listview .getSelectionModel .getSelectedIndex) item (-> listview .getSelectionModel .getSelectedItem) new-index (if (= direction :up) (dec index) (inc index))] (when-not (or (= -1 new-index) (= (count items) new-index)) (.remove items index (inc index)) (.add items new-index item) (.selectIndices (.getSelectionModel listview) new-index (int-array []))))) (defn source-add [instance event] (let [source-input ^TextField (.-sourceInput instance)] (.add (.getItems ^ListView (.-sourceList instance)) (.getText source-input)) (.setText source-input ""))) (defn add-group [instance event] (let [group-input ^TextField (.-groupInput instance)] (.add (.getItems ^ListView (.-groupList instance)) (.getText group-input)) (.setText group-input ""))) (defn rm-group [instance event] (let [group-input ^TextField (.-groupInput instance) item (-> instance .-groupList .getSelectionModel .getSelectedItem)] (.setText group-input "") (.remove ^List (.getItems (.-groupList instance)) ^String item))) (defn source-del [instance event] (let [source-input ^TextField (.-sourceInput instance) item (-> instance .sourceList .getSelectionModel .getSelectedItem)] (.setText source-input "") (.remove ^List (.getItems (.-sourceList instance)) ^String item))) (defn source-mv-up [instance event] (move-listview-item (.-sourceList instance) :up)) (defn source-mv-down [instance event] (move-listview-item (.-sourceList instance) :down)) (defn mv-up-group [instance event] (move-listview-item (.-groupList instance) :up)) (defn mv-down-group [instance event] (move-listview-item (.-groupList instance) :down))
4.3 Storage
(defn save-supervisor-pane [settings instance] (let [activated (.isSelected (.-supervisorState instance)) items (.getItems (.-sourceList instance))] (-> settings (assoc-in [:supervisor :activated] activated) (assoc-in [:supervisor :videosources] (into [] items))))) (defn save [instance event] (reset! (:settings @settings) (-> @(:settings @settings) (assoc-in [:form :room] (.getText (.-roomNumber instance))) (assoc-in [:form :groups] (into [] (.getItems (.-groupList instance)))) (assoc :camera {:type (keyword (.getText (.-camType instance))) :address (.getText (.-camAddress instance)) :username (.getText (.-camUsername instance)) :password (.getText (.-camPassword instance))}) (assoc :configuration (keyword (.getValue (.-configBox instance)))) (assoc :smb {:username (.getText (.-smbUser instance)) :password (.getText (.-smbPassword instance)) :dns (.getText (.-smbServer instance)) :directory (.getText (.-smbDir instance))}) (save-supervisor-pane instance))) (pprint/pprint @(:settings @settings) (io/writer "settings.edn")) (-> instance .-toplevel .getScene .getWindow .hide)) (defn cancel [instance event] (-> instance .-toplevel .getScene .getWindow .hide))
5 Complete namespace definition
(ns videocapture.settings (:require [clojure.edn :as edn] [clojure.string :as str] [clojure.spec.alpha :as s] [clojure.java.io :as io] [clojurefx.fxml :as fxml] [clojure.pprint :as pprint] [mount.core :as mount] [clojurefx.clojurefx :as fx] [taoensso.timbre :as timbre :refer [log trace debug info warn error fatal report logf tracef debugf infof warnf errorf fatalf reportf spy get-env]] [videocapture.version :as version] [videocapture.gui-util :as gui-util] ) (:import (javafx.stage Stage Window Modality) (javafx.scene.control ListView SelectionMode TextField ChoiceBox) (java.util List) (java.nio.file Files))) (declare settings) <<types>> <<settings>> <<gui-init>> <<gui-manipulate>> <<gui-save>>