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>>