Update the Logger component.
[master-thesis.git] / Parasitemia / ParasitemiaUI / Analysis.fs
index 9b9ed51..e276be6 100644 (file)
@@ -18,39 +18,31 @@ open ParasitemiaCore.Config
 
 open Types
 
-let showWindow (parent: Window) (state: State.State) : bool =
-    let window = Views.AnalysisWindow()
-    window.Root.Owner <- parent
-    window.Root.Left <- parent.Left + parent.ActualWidth / 2. - window.Root.Width / 2.
-    window.Root.Top <- parent.Top + parent.ActualHeight / 2. - window.Root.Height / 2.
-
-    let ctrl (name: string): 'a = window.Root.FindName(name) :?> 'a
-
-    let butClose: Button = ctrl "butClose"
-    let butStart: Button = ctrl "butStart"
-
-    let stackSourceImagesSelection: StackPanel = ctrl "stackSourceImagesSelection"
-    let progressBar: ProgressBar = ctrl "progress"
-    let textLog: TextBlock = ctrl "textLog"
-    let scrollLog: ScrollViewer = ctrl "scrollLog"
+let showWindow (parent : Window) (state : State.State) : bool =
+    let win = Views.AnalysisWindow()
+    win.Owner <- parent
+    win.Left <- (if parent.WindowState = WindowState.Maximized then 0. else parent.Left) + parent.ActualWidth / 2. - win.Width / 2.
+    win.Top <- (if parent.WindowState = WindowState.Maximized then 0. else parent.Top) + parent.ActualHeight / 2. - win.Height / 2.
 
     let logListener =
-        { new Logger.IListener with
-            member this.NewEntry severity mess =
-                window.Root.Dispatcher.Invoke(fun () ->
-                textLog.Inlines.Add(Documents.Run(mess))
-                textLog.Inlines.Add(Documents.LineBreak())
-                scrollLog.ScrollToBottom()) }
+        {
+            new Logger.IListener with
+                member this.NewEntry severity _header mess =
+                    win.Dispatcher.Invoke(fun () ->
+                        win.textLog.Inlines.Add(Documents.Run(mess))
+                        win.textLog.Inlines.Add(Documents.LineBreak())
+                        win.scrollLog.ScrollToBottom()
+                    )
+        }
 
     Logger.Log.AddListener(logListener)
 
     let minPPI = 1.
     let maxPPI = 10e6
-    let parseAndValidatePPI (input: string) : float option =
-        let res = ref 0.
-        if Double.TryParse(input, res) && !res >= minPPI && !res <= maxPPI
-        then Some !res
-        else None
+    let parseAndValidatePPI (input : string) : float option =
+        match Double.TryParse(input) with
+        | true, value when value >= minPPI && value <= maxPPI -> Some value
+        | _ -> None
 
     let monitor = Object()
     let mutable atLeastOneAnalysisPerformed = false
@@ -58,8 +50,8 @@ let showWindow (parent: Window) (state: State.State) : bool =
     let mutable analysisCancelled = false
 
     let updateSourceImages () =
-        stackSourceImagesSelection.Children.Clear()
-        let width = int stackSourceImagesSelection.ActualWidth
+        win.stackSourceImagesSelection.Children.Clear()
+        let width = int win.stackSourceImagesSelection.ActualWidth
         for srcImg in state.SourceImages do
             let imageSourceSelection = Views.ImageSourceSelection(Tag = srcImg, Margin = Thickness(3.))
             imageSourceSelection.Tag <- srcImg
@@ -71,8 +63,17 @@ let showWindow (parent: Window) (state: State.State) : bool =
             imageSourceSelection.lblDateLastAnalysis.Content <- if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToString()
 
             imageSourceSelection.txtResolution.Text <- if srcImg.dateLastAnalysis.Ticks = 0L then "" else srcImg.config.Parameters.resolution.ToString()
-            imageSourceSelection.menuZoom50X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "230000")
-            imageSourceSelection.menuZoom100X.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- "460000")
+
+            for ppi in Utils.predefinedPPI do
+                let menu = MenuItem()
+                menu.Header <- ppi.ToString()
+                menu.Click.AddHandler(fun obj args -> imageSourceSelection.txtResolution.Text <- ppi.ppi.ToString())
+                imageSourceSelection.predefinedValuesMenu.Items.Add(menu) |> ignore
+
+            imageSourceSelection.butPPICalculator.Click.AddHandler(fun obj args ->
+                match PPICalculator.showWindow win with
+                | Some resolution -> imageSourceSelection.txtResolution.Text <- resolution.ToString()
+                | None -> ())
 
             imageSourceSelection.txtResolution.PreviewTextInput.AddHandler(fun obj args ->
                 let text = imageSourceSelection.txtResolution.Text + args.Text
@@ -82,51 +83,56 @@ let showWindow (parent: Window) (state: State.State) : bool =
                 let checkbox = imageSourceSelection.chkSelection
                 checkbox.IsChecked <- Nullable<bool>(not (checkbox.IsChecked.HasValue && checkbox.IsChecked.Value)))
 
-            stackSourceImagesSelection.Children.Add(imageSourceSelection) |> ignore
+            win.stackSourceImagesSelection.Children.Add(imageSourceSelection) |> ignore
 
     // Get the new parameters for each image. If an error occurs then 'None' is returned and a message box is displayed.
     // The boolean is 'true' if the image is selected (checked).
     let getInputImagesParameters () : (SourceImage * bool * Parameters) list option =
-        let sourceImagesControls = stackSourceImagesSelection.Children |> Seq.cast<Views.ImageSourceSelection>
-        let parameters = seq {
-            for srcImgCtrl in sourceImagesControls do
-                let srcImg = srcImgCtrl.Tag :?> SourceImage
-                let isChecked = srcImgCtrl.chkSelection.IsChecked
-                match parseAndValidatePPI srcImgCtrl.txtResolution.Text with
-                    | Some resolution ->
-                        yield Some (srcImg, isChecked.HasValue && isChecked.Value, { srcImg.config.Parameters with resolution = resolution * 1.<ppi> })
-                    | None ->
-                        MessageBox.Show(sprintf "No resolution defined for the image number %d" srcImg.num, "No resolution defined", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
-                        yield None } |> Seq.takeWhile (fun e -> e.IsSome) |> Seq.map (fun e -> e.Value) |> List.ofSeq
-        if parameters.Count() <> sourceImagesControls.Count()
-        then None
-        else Some parameters
-
-    butClose.Click.AddHandler(fun obj args -> window.Root.Close())
-
-    butStart.Click.AddHandler(fun obj args ->
+        let sourceImagesControls = win.stackSourceImagesSelection.Children |> Seq.cast<Views.ImageSourceSelection>
+        let parameters =
+            seq {
+                for srcImgCtrl in sourceImagesControls do
+                    let srcImg = srcImgCtrl.Tag :?> SourceImage
+                    let isChecked = srcImgCtrl.chkSelection.IsChecked
+                    match parseAndValidatePPI srcImgCtrl.txtResolution.Text with
+                        | Some resolution ->
+                            yield Some (srcImg, isChecked.HasValue && isChecked.Value, { srcImg.config.Parameters with resolution = resolution * 1.<ppi> })
+                        | None ->
+                            MessageBox.Show(sprintf "No resolution defined for the image number %d" srcImg.num, "No resolution defined", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
+                            yield None
+            } |> Seq.takeWhile (fun e -> e.IsSome) |> Seq.map (fun e -> e.Value) |> List.ofSeq
+
+        if parameters.Count() <> sourceImagesControls.Count() then
+            None
+        else
+            Some parameters
+
+    win.butClose.Click.AddHandler(fun obj args -> win.Close())
+
+    win.butStart.Click.AddHandler(fun obj args ->
         match getInputImagesParameters () with
         | Some imagesParameters ->
-            let imagesToProcess = [
-                for srcImg, selected, parameters in imagesParameters do
-                    srcImg.config.Parameters <- parameters // Save parameters.
-                    if selected
-                    then yield srcImg.num.ToString(), srcImg.config, srcImg.img ]
-
-            if imagesToProcess.IsEmpty
-            then
+            let imagesToProcess =
+                [
+                    for srcImg, selected, parameters in imagesParameters do
+                        srcImg.config.Parameters <- parameters // Save parameters.
+                        if selected then
+                            yield srcImg.num.ToString(), srcImg.config, srcImg.img
+                ]
+
+            if imagesToProcess.IsEmpty then
                 MessageBox.Show("No image selected", "Cannot start analysis", MessageBoxButton.OK, MessageBoxImage.Information) |> ignore
             else
-                stackSourceImagesSelection.IsEnabled <- false
+                win.stackSourceImagesSelection.IsEnabled <- false
                 analysisPerformed <- false
-                butStart.IsEnabled <- false
-                butClose.Content <- "Abort"
+                win.butStart.IsEnabled <- false
+                win.butClose.Content <- "Abort"
 
                 async {
                     let maybeResults =
                         ParasitemiaCore.Analysis.doMultipleAnalysis
                             imagesToProcess
-                            (Some (fun progress -> window.Root.Dispatcher.Invoke(fun () -> progressBar.Value <- float progress); not analysisCancelled))
+                            (Some (fun progress -> win.Dispatcher.Invoke(fun () -> win.progress.Value <- float progress); not analysisCancelled))
 
                     lock monitor (
                         fun() ->
@@ -134,28 +140,34 @@ let showWindow (parent: Window) (state: State.State) : bool =
                             | Some results ->
                                 for id, cells in results do
                                     state.SetResult (int id) cells
-
-                                window.Root.Dispatcher.Invoke(fun () ->
-                                    stackSourceImagesSelection.IsEnabled <- true
-                                    butStart.IsEnabled <- true
-                                    butClose.Content <- "Close"
-                                    updateSourceImages ())
-
-                                Logger.Log.User("All analyses terminated successfully")
+                                Logger.Log.Info "All analyses terminated successfully"
                                 atLeastOneAnalysisPerformed <- true
                                 analysisPerformed <- true
-                            | None -> ())
+                            | None ->
+                                Logger.Log.Info "Analysis aborted"
+
+                            win.Dispatcher.Invoke(fun () ->
+                                win.progress.Value <- if maybeResults.IsSome then 100. else 0.
+                                win.stackSourceImagesSelection.IsEnabled <- true
+                                win.butStart.IsEnabled <- true
+                                win.butClose.Content <- "Close"
+                                updateSourceImages ()
+                            )
+                    )
                 } |> Async.Start
-        | _ -> ())
+        | _ -> ()
+    )
 
-    window.Root.Loaded.AddHandler(fun obj args -> updateSourceImages ())
+    win.Loaded.AddHandler(fun obj args -> updateSourceImages ())
 
-    window.Root.ShowDialog() |> ignore
+    win.ShowDialog() |> ignore
 
     Logger.Log.RmListener(logListener)
 
-    lock monitor (fun () ->
-        if not analysisPerformed
-            then
+    lock monitor (
+        fun () ->
+            if not analysisPerformed then
+                // To cancel the current analysis if one is running on the next call to the progress function.
                 analysisCancelled <- true
-        atLeastOneAnalysisPerformed)
+            atLeastOneAnalysisPerformed
+    )