let menuExit: MenuItem = ctrl "menuExit"
let menuSaveFile: MenuItem = ctrl "menuSave"
+ let menuSaveAsFile: MenuItem = ctrl "menuSaveAs"
let menuLoadFile: MenuItem = ctrl "menuOpen"
let menuNewFile: MenuItem = ctrl "menuNew"
let menuAddSourceImage: MenuItem = ctrl "menuAddSourceImage"
let menuHightlightRBC: MenuItem = ctrl "menuHightlightRBC"
let menuAbout: MenuItem = ctrl "menuAbout"
+ let txtDocumentStatus: TextBlock = ctrl "txtDocumentStatus"
+ let txtMessageStatus: TextBlock = ctrl "txtMessageStatus"
+
let txtPatient: TextBox = ctrl "txtPatient"
let txtGlobalParasitemia: TextBox = ctrl "txtGlobalParasitemia"
let scrollViewCurrentImage: ScrollViewer = ctrl "scrollViewCurrentImage"
let borderCurrentImage: Border = ctrl "borderCurrentImage"
let canvasCurrentImage: Canvas = ctrl "canvasCurrentImage"
- let txtImageInformation: TextBlock = ctrl "txtImageInformation"
+ let txtImageInformation1: TextBlock = ctrl "txtImageInformation1"
+ let txtImageInformation2: TextBlock = ctrl "txtImageInformation2"
let scrollRBC: ScrollViewer = ctrl "scrollRBC"
let stackRBC: StackPanel = ctrl "stackRBC"
frame.txtRBCNumber.Text <- rbc.num.ToString()
frame
+ let updateDocumentStatus () =
+ txtDocumentStatus.Text <- if state.FilePath = "" then "<New document>" else state.FilePath
+
+ let statusMessageTimer = Threading.DispatcherTimer()
+ statusMessageTimer.Tick.AddHandler(fun obj args -> statusMessageTimer.Stop(); txtMessageStatus.Text <- "")
+ statusMessageTimer.Interval <- TimeSpan(0, 0, 2)
+ let displayStatusMessage (message: string) =
+ txtMessageStatus.Text <- message
+ statusMessageTimer.Stop()
+ statusMessageTimer.Start()
+
let highlightRBCFrame (frame: Views.RBCFrame) (highlight: bool) =
let rbc = frame.Tag :?> RBC
if highlight
scrollViewCurrentImage.ScrollToHorizontalOffset(rbc.center.X * currentScale - scrollViewCurrentImage.ViewportWidth / 2. + borderCurrentImage.BorderThickness.Left)
scrollViewCurrentImage.ScrollToVerticalOffset(rbc.center.Y * currentScale - scrollViewCurrentImage.ViewportHeight / 2. + borderCurrentImage.BorderThickness.Top)
- let parasitemiaText (nbTotal: int, nbInfected: int) : string =
+ let percentText (nbTotal: int, nb: int) : string =
if nbTotal = 0
then
""
else
- let percent = 100. * (float nbInfected) / (float nbTotal)
- sprintf "%.1f %% (%d / %d)" percent nbInfected nbTotal
+ let percent = 100. * (float nb) / (float nbTotal)
+ sprintf "%.1f %% (%d / %d)" percent nb nbTotal
let updateCurrentImageInformation () =
match state.CurrentImage with
| Some srcImg ->
- let parasitemiaStr = parasitemiaText (state.ImageParasitemia srcImg)
- txtImageInformation.Inlines.Clear()
- txtImageInformation.Inlines.Add(Documents.Run("Parasitemia: ", FontWeight = FontWeights.Bold))
- txtImageInformation.Inlines.Add(parasitemiaStr)
- txtImageInformation.Inlines.Add(Documents.LineBreak())
-
- txtImageInformation.Inlines.Add(Documents.Run("Average erytrocyte diameter: ", FontWeight = FontWeights.Bold))
- txtImageInformation.Inlines.Add(Documents.Run(srcImg.config.RBCRadius.ToString()))
- txtImageInformation.Inlines.Add(Documents.LineBreak())
-
- txtImageInformation.Inlines.Add(Documents.Run("Last analysis: ", FontWeight = FontWeights.Bold))
- txtImageInformation.Inlines.Add(Documents.Run(if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToLocalTime().ToString()))
+ let parasitemiaStr = percentText (state.ImageParasitemia srcImg)
+ txtImageInformation1.Inlines.Clear()
+ txtImageInformation1.Inlines.Add(Documents.Run("Parasitemia: ", FontWeight = FontWeights.Bold))
+ txtImageInformation1.Inlines.Add(parasitemiaStr)
+ txtImageInformation1.Inlines.Add(Documents.LineBreak())
+
+ txtImageInformation1.Inlines.Add(Documents.Run("Last analysis: ", FontWeight = FontWeights.Bold))
+ txtImageInformation1.Inlines.Add(Documents.Run(if srcImg.dateLastAnalysis.Ticks = 0L then "<Never>" else srcImg.dateLastAnalysis.ToLocalTime().ToString()))
+
+ txtImageInformation2.Inlines.Clear()
+ let alteredStr = percentText (state.ImageNbAltered srcImg)
+ txtImageInformation2.Inlines.Add(Documents.Run("Number of erytrocytes manually altered: ", FontWeight = FontWeights.Bold))
+ txtImageInformation2.Inlines.Add(Documents.Run(alteredStr))
+ txtImageInformation2.Inlines.Add(Documents.LineBreak())
+
+ txtImageInformation2.Inlines.Add(Documents.Run("Average erytrocyte diameter: ", FontWeight = FontWeights.Bold))
+ txtImageInformation2.Inlines.Add(Documents.Run(srcImg.config.RBCRadius.ToString()))
| _ -> ()
let updateGlobalParasitemia () =
- txtGlobalParasitemia.Text <- parasitemiaText state.GlobalParasitemia
+ txtGlobalParasitemia.Text <- percentText state.GlobalParasitemia
let updateViewportPreview () =
for preview in stackPreviews.Children |> Seq.cast<Views.ImageSourcePreview> do
canvasCurrentImage.Children.[i].Visibility <- Visibility.Hidden
| _ -> ()
+ let askDocumentPathToSave () : string option =
+ let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter)
+
+ if state.FilePath <> ""
+ then
+ dialog.FileName <- FileInfo(state.FilePath).Name
+ elif state.PatientID <> ""
+ then
+ dialog.FileName <- state.PatientID + PiaZ.extension
+
+ let res = dialog.ShowDialog()
+ if res.HasValue && res.Value then
+ Some dialog.FileName
+ else
+ None
+
let saveCurrentDocument () =
try
if state.FilePath = ""
then
- let dialog = SaveFileDialog(AddExtension = true, DefaultExt = PiaZ.extension, Filter = PiaZ.filter);
- let res = dialog.ShowDialog()
- if res.HasValue && res.Value
- then
- state.FilePath <- dialog.FileName
+ match askDocumentPathToSave () with
+ | Some filepath ->
+ state.FilePath <- filepath
state.Save()
+ | _ -> ()
else
state.Save()
+ updateDocumentStatus ()
+ displayStatusMessage "Document saved"
with
| :? IOException as ex ->
Log.Error(ex.ToString())
MessageBox.Show(sprintf "The document cannot be save in '%s'" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
+ let saveCurrentDocumentAsNewFile () =
+ match askDocumentPathToSave () with
+ | Some filepath ->
+ state.FilePath <- filepath
+ saveCurrentDocument ()
+ | _ -> ()
+
// Ask the use to save the current document if neccessary.
let askSaveCurrent () =
if state.AlteredSinceLastSave
txtPatient.Text <- state.PatientID
updatePreviews ()
updateGlobalParasitemia ()
+ updateDocumentStatus ()
let loadFile (filepath: string) =
askSaveCurrent ()
| :? IOException as ex ->
Log.Error(ex.ToString())
state.FilePath <- previousFilePath
- MessageBox.Show(sprintf "The document cannot be loaded from '%s'" state.FilePath, "Error saving the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
+ MessageBox.Show(sprintf "The document cannot be loaded from '%s'" state.FilePath, "Error loading the document", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
- txtPatient.LostFocus.AddHandler(fun obj args -> state.PatientID <- txtPatient.Text)
+ txtPatient.TextChanged.AddHandler(fun obj args -> state.PatientID <- txtPatient.Text)
menuExit.Click.AddHandler(fun obj args ->
askSaveCurrent ()
mainWindow.Root.Close())
menuSaveFile.Click.AddHandler(fun obj args -> saveCurrentDocument ())
+ menuSaveAsFile.Click.AddHandler(fun obj args -> saveCurrentDocumentAsNewFile ())
menuLoadFile.Click.AddHandler(fun obj args ->
// TODO: if current state not saved and not empty, ask to save it.
let noSourceImage = state.SourceImages.Count() = 0
for filename in dialog.FileNames do
- let srcImg = state.AddSourceImage filename defaultConfig
- addPreview srcImg
+ try
+ let srcImg = state.AddSourceImage filename defaultConfig
+ addPreview srcImg
+ with
+ | _ as ex ->
+ Log.Error(ex.ToString())
+ MessageBox.Show(sprintf "Unable to read the image from '%s'" filename, "Error adding an image", MessageBoxButton.OK, MessageBoxImage.Error) |> ignore
updateGlobalParasitemia ()
borderCurrentImage.ReleaseMouseCapture()
args.Handled <- true)
+ // Shortcuts.
+ mainWindow.Root.InputBindings.Add(
+ Input.KeyBinding(
+ FSharp.ViewModule.FunCommand((fun obj -> saveCurrentDocument ()), (fun obj -> true)),
+ Input.KeyGesture(Input.Key.S, Input.ModifierKeys.Control))) |> ignore
+
// Viewport preview.
scrollViewCurrentImage.ScrollChanged.AddHandler(fun obj args -> updateViewportPreview ())
+ updateDocumentStatus ()
+
mainWindow.Root.Show()
match fileToOpen with
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
- x:Name="MainWindow" Height="681.888" Width="787.61" MinHeight="200" MinWidth="300" Title="Parasitemia" Icon="pack://application:,,,/Resources/icon.ico">
+ x:Name="MainWindow" Height="700" Width="1000" MinHeight="200" MinWidth="300" Title="Parasitemia" Icon="pack://application:,,,/Resources/icon.ico" ResizeMode="CanResizeWithGrip">
<DockPanel x:Name="dockPanelMain" LastChildFill="True">
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem x:Name="menuNew" Header="_New" />
<MenuItem x:Name="menuOpen" Header="_Open" />
<MenuItem x:Name="menuSave" Header="_Save" />
+ <MenuItem x:Name="menuSaveAs" Header="_Save As..." />
<Separator />
<MenuItem x:Name="menuExit" Header="_Exit" />
</MenuItem>
<MenuItem x:Name="menuAbout" Header="_About" />
</MenuItem>
</Menu>
+ <StatusBar DockPanel.Dock="Bottom" >
+ <StatusBarItem>
+ <TextBlock Name="txtDocumentStatus" />
+ </StatusBarItem>
+ <StatusBarItem>
+ <TextBlock Name="txtMessageStatus" />
+ </StatusBarItem>
+ </StatusBar>
<Grid x:Name="gridMain">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<ScrollViewer x:Name="scrollRBC" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Visible" Grid.RowSpan="1" Margin="3">
<StackPanel x:Name="stackRBC" Orientation="Horizontal" />
</ScrollViewer>
- <TextBlock x:Name="txtImageInformation" Grid.Row="2" TextWrapping="Wrap" Margin="3" />
+ <Grid Grid.Row="2">
+ <Grid.ColumnDefinitions>
+ <ColumnDefinition Width="1*"/>
+ <ColumnDefinition Width="1*"/>
+ </Grid.ColumnDefinitions>
+ <TextBlock x:Name="txtImageInformation1" TextWrapping="Wrap" Margin="3" Grid.Column="0" />
+ <TextBlock x:Name="txtImageInformation2" TextWrapping="Wrap" Margin="3" Grid.Column="1" />
+ </Grid>
</Grid>
</Grid>
</DockPanel>