mirror of
https://github.com/13hannes11/focus_annotator.git
synced 2024-09-03 23:21:01 +02:00
use message passing instead of Arc to avoid panics
This commit is contained in:
175
src/main.rs
175
src/main.rs
@@ -1,28 +1,19 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate derive_builder;
|
extern crate derive_builder;
|
||||||
|
|
||||||
|
mod constants;
|
||||||
mod state;
|
mod state;
|
||||||
mod ui;
|
mod ui;
|
||||||
mod constants;
|
|
||||||
|
|
||||||
pub use crate::state::AnnotationImage;
|
|
||||||
pub use crate::constants::MARGIN_BOTTOM;
|
pub use crate::constants::MARGIN_BOTTOM;
|
||||||
use crate::state::AnnotationZStack;
|
pub use crate::state::AnnotationImage;
|
||||||
pub use crate::ui::ImageUI;
|
pub use crate::ui::ImageUI;
|
||||||
|
|
||||||
|
|
||||||
use std::cell::{RefCell};
|
|
||||||
use std::fs;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::{Arc};
|
|
||||||
|
|
||||||
use adw::{prelude::*, Application};
|
use adw::{prelude::*, Application};
|
||||||
use constants::{TOGGLE_NEIGHBOURS_TEXT_TOGGLED, TOGGLE_NEIGHBOURS_TEXT, SCALE_STEP};
|
|
||||||
use glib::clone;
|
|
||||||
use gtk::gio::SimpleAction;
|
use gtk::gio::SimpleAction;
|
||||||
use gtk::{glib, FileChooserDialog, FileChooserAction, ResponseType, FileFilter};
|
use gtk::glib::{MainContext, PRIORITY_DEFAULT};
|
||||||
|
|
||||||
use state::{State};
|
use state::{Message, State, UIMessage};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let application = Application::builder()
|
let application = Application::builder()
|
||||||
@@ -39,147 +30,100 @@ fn main() {
|
|||||||
application.run();
|
application.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn build_ui(app: &Application) {
|
fn build_ui(app: &Application) {
|
||||||
|
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
|
||||||
|
|
||||||
let state = Arc::new(RefCell::new(State::new()));
|
let mut state = State::new();
|
||||||
|
let image_ui = ImageUI::new(app, sender.clone());
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
// MAIN CONTENT //
|
// MAIN CONTENT //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
let image_ui = Arc::new(ImageUI::new(app));
|
|
||||||
//image_ui.build_ui();
|
//image_ui.build_ui();
|
||||||
|
|
||||||
image_ui.focus_scale.connect_value_changed(clone!(@strong image_ui, @strong state => move |x| {
|
let _sender = sender.clone();
|
||||||
|
image_ui.focus_scale.connect_value_changed(move |x| {
|
||||||
let index = x.value() as usize;
|
let index = x.value() as usize;
|
||||||
state.borrow_mut().set_image_index(Some(index));
|
_sender.send(Message::FocusLevelChange(index)).unwrap();
|
||||||
image_ui.update(&state.borrow());
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// BOTTOM TOOLBAR //
|
// BOTTOM TOOLBAR //
|
||||||
///////////////////
|
///////////////////
|
||||||
image_ui.back_button.connect_clicked(|button| {
|
image_ui.back_button.connect_clicked(|button| {
|
||||||
button.activate_action("win.back_focus", None)
|
button
|
||||||
|
.activate_action("win.back_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
|
|
||||||
image_ui.skip_button.connect_clicked(|button| {
|
image_ui.skip_button.connect_clicked(|button| {
|
||||||
button.activate_action("win.skip_focus", None)
|
button
|
||||||
|
.activate_action("win.skip_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
|
|
||||||
image_ui.focus_button.connect_clicked(|button| {
|
image_ui.focus_button.connect_clicked(|button| {
|
||||||
button.activate_action("win.mark_focus", None)
|
button
|
||||||
|
.activate_action("win.mark_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
|
|
||||||
let focus_image = image_ui.individual.clone();
|
let _sender = sender.clone();
|
||||||
image_ui.neighbour_toggle_button.connect_toggled(
|
image_ui.neighbour_toggle_button.connect_toggled(move |x| {
|
||||||
clone!(@strong image_ui => move |x| match x.is_active() {
|
_sender
|
||||||
true => {
|
.send(Message::UI(UIMessage::ShowGrid(x.is_active())))
|
||||||
image_ui.focus_neighbours_aspect_frame.set_child(Some(image_ui.focus_neighbours_grid.as_ref()));
|
.unwrap();
|
||||||
x.set_label(TOGGLE_NEIGHBOURS_TEXT_TOGGLED);
|
});
|
||||||
}
|
|
||||||
false => {
|
|
||||||
image_ui.focus_neighbours_aspect_frame.set_child(Some(focus_image.as_ref()));
|
|
||||||
x.set_label(TOGGLE_NEIGHBOURS_TEXT);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
image_ui.open_button.connect_clicked(clone!(@strong image_ui, @strong state => move |_| {
|
let _sender = sender.clone();
|
||||||
// TODO: actually open and load data
|
image_ui.open_button.connect_clicked(move |_| {
|
||||||
|
_sender
|
||||||
|
.send(Message::UI(UIMessage::OpenFileChooser))
|
||||||
let file_chooser_action = FileChooserAction::Open;
|
.unwrap();
|
||||||
let buttons = [("Open", ResponseType::Ok), ("Cancel", ResponseType::Cancel)];
|
});
|
||||||
let filter = FileFilter::new();
|
|
||||||
filter.add_pattern(r"*.json");
|
|
||||||
|
|
||||||
let file_chooser = Arc::new(FileChooserDialog::new(Some("Chose a data file!"), Some(image_ui.window.as_ref()), file_chooser_action, &buttons));
|
|
||||||
file_chooser.set_select_multiple(false);
|
|
||||||
file_chooser.set_filter(&filter);
|
|
||||||
|
|
||||||
file_chooser.connect_response(clone!(@strong image_ui, @weak state => move |dialog: &FileChooserDialog, response: ResponseType| {
|
|
||||||
if response == ResponseType::Ok {
|
|
||||||
let file = dialog.file().expect("Couldn't get file");
|
|
||||||
eprintln!("Open");
|
|
||||||
let filename = file.path().expect("Couldn't get file path");
|
|
||||||
let contents = fs::read_to_string(filename.clone()).expect("Something went wrong reading the file");
|
|
||||||
eprintln!("{}", contents);
|
|
||||||
|
|
||||||
let new_dataset : Vec<AnnotationZStack> = serde_json::from_str(&contents).unwrap();
|
|
||||||
let mut state = state.borrow_mut();
|
|
||||||
|
|
||||||
state.replace_foucs_stacks(new_dataset);
|
|
||||||
state.file_name = filename.clone().as_path().file_name().map(|x| x.to_str().expect("failed to convert filname to str").to_string());
|
|
||||||
state.root_path = filename.clone().as_path().parent().map(|x| x.to_str().expect("failed to convert filname to str").to_string());
|
|
||||||
|
|
||||||
match (state.root_path.clone(), state.file_name.clone()) {
|
|
||||||
(Some(root_path), Some(file_name)) => {
|
|
||||||
let path = Path::new(&root_path).join(Path::new(&file_name));
|
|
||||||
eprintln!("{:?}", path);
|
|
||||||
}
|
|
||||||
(_,_) => {
|
|
||||||
eprintln!("Path not properly set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
image_ui.update(&state);
|
|
||||||
}
|
|
||||||
dialog.close();
|
|
||||||
}));
|
|
||||||
|
|
||||||
file_chooser.show();
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
// Keyboard Shortcuts //
|
// Keyboard Shortcuts //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
let _sender = sender.clone();
|
||||||
let action_toggle_neighbour = SimpleAction::new("toggle_neighbour", None);
|
let action_toggle_neighbour = SimpleAction::new("toggle_neighbour", None);
|
||||||
action_toggle_neighbour.connect_activate(clone!(@strong image_ui => move |_, _| {
|
action_toggle_neighbour
|
||||||
image_ui.neighbour_toggle_button.set_active(!image_ui.neighbour_toggle_button.is_active());
|
.connect_activate(move |_, _| _sender.send(Message::UI(UIMessage::ToggleGrid)).unwrap());
|
||||||
}));
|
|
||||||
|
|
||||||
|
let _sender = sender.clone();
|
||||||
let action_focus_scale_increment = SimpleAction::new("increment_focus_scale", None);
|
let action_focus_scale_increment = SimpleAction::new("increment_focus_scale", None);
|
||||||
action_focus_scale_increment.connect_activate(clone!(@strong image_ui => move |_, _| {
|
action_focus_scale_increment.connect_activate(move |_, _| {
|
||||||
image_ui.focus_scale.set_value(image_ui.focus_scale.value() + SCALE_STEP);
|
_sender
|
||||||
}));
|
.send(Message::UI(UIMessage::IncrementFocus))
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let _sender = sender.clone();
|
||||||
let action_focus_scale_decrement = SimpleAction::new("decrement_focus_scale", None);
|
let action_focus_scale_decrement = SimpleAction::new("decrement_focus_scale", None);
|
||||||
action_focus_scale_decrement.connect_activate(clone!(@strong image_ui => move |_, _| {
|
action_focus_scale_decrement.connect_activate(move |_, _| {
|
||||||
image_ui.focus_scale.set_value(image_ui.focus_scale.value() - SCALE_STEP);
|
_sender
|
||||||
}));
|
.send(Message::UI(UIMessage::DecrementFocus))
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let _sender = sender.clone();
|
||||||
let mark_focus = SimpleAction::new("mark_focus", None);
|
let mark_focus = SimpleAction::new("mark_focus", None);
|
||||||
mark_focus.connect_activate(clone!(@strong image_ui, @strong state => move |_, _| {
|
mark_focus.connect_activate(move |_, _| {
|
||||||
eprintln! {"Focus Set!"};
|
_sender.send(Message::MarkFocus).unwrap();
|
||||||
|
});
|
||||||
let mut state = state.borrow_mut();
|
|
||||||
state.mark_focus();
|
|
||||||
state.save();
|
|
||||||
state.skip();
|
|
||||||
image_ui.update(&state);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
let _sender = sender.clone();
|
||||||
let skip_focus = SimpleAction::new("skip_focus", None);
|
let skip_focus = SimpleAction::new("skip_focus", None);
|
||||||
skip_focus.connect_activate(clone!(@strong image_ui, @strong state => move |_, _| {
|
skip_focus.connect_activate(move |_, _| {
|
||||||
let mut state = state.borrow_mut();
|
_sender.send(Message::NextImage).unwrap();
|
||||||
state.skip();
|
});
|
||||||
image_ui.update(&state);
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
let _sender = sender.clone();
|
||||||
let back_focus = SimpleAction::new("back_focus", None);
|
let back_focus = SimpleAction::new("back_focus", None);
|
||||||
back_focus.connect_activate(clone!(@strong image_ui, @strong state => move |_, _| {
|
back_focus.connect_activate(move |_, _| {
|
||||||
let mut state = state.borrow_mut();
|
_sender.send(Message::PreviousImage).unwrap();
|
||||||
state.previous();
|
});
|
||||||
image_ui.update(&state);
|
|
||||||
}));
|
|
||||||
|
|
||||||
image_ui.window.add_action(&action_toggle_neighbour);
|
image_ui.window.add_action(&action_toggle_neighbour);
|
||||||
image_ui.window.add_action(&action_focus_scale_increment);
|
image_ui.window.add_action(&action_focus_scale_increment);
|
||||||
@@ -187,5 +131,12 @@ fn build_ui(app: &Application) {
|
|||||||
image_ui.window.add_action(&mark_focus);
|
image_ui.window.add_action(&mark_focus);
|
||||||
image_ui.window.add_action(&skip_focus);
|
image_ui.window.add_action(&skip_focus);
|
||||||
image_ui.window.add_action(&back_focus);
|
image_ui.window.add_action(&back_focus);
|
||||||
|
|
||||||
image_ui.show();
|
image_ui.show();
|
||||||
|
receiver.attach(None, move |msg| {
|
||||||
|
eprintln!("Received message: {:?}", msg);
|
||||||
|
state.update(&msg);
|
||||||
|
image_ui.refresh(&msg, &state);
|
||||||
|
Continue(true)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
108
src/state/mod.rs
108
src/state/mod.rs
@@ -1,17 +1,41 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::{collections::HashMap, fs::File};
|
|
||||||
|
|
||||||
|
use gtk::{gio::File, prelude::FileExt};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::constants::NONE_STRING_OPTION;
|
use crate::constants::NONE_STRING_OPTION;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Message {
|
||||||
|
FocusLevelChange(usize),
|
||||||
|
MarkFocus,
|
||||||
|
NextImage,
|
||||||
|
PreviousImage,
|
||||||
|
UI(UIMessage),
|
||||||
|
OpenFile(File),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Messages that do not impact state
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UIMessage {
|
||||||
|
OpenFileChooser,
|
||||||
|
RefreshImages,
|
||||||
|
ToggleGrid,
|
||||||
|
DecrementFocus,
|
||||||
|
IncrementFocus,
|
||||||
|
ShowGrid(bool),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
stacks: Vec<AnnotationZStack>,
|
stacks: Vec<AnnotationZStack>,
|
||||||
stack_index: Option<usize>,
|
stack_index: Option<usize>,
|
||||||
pub image_index: Option<usize>,
|
focus_image_index: Option<usize>,
|
||||||
pub file_name: Option<String>,
|
file_name: Option<String>,
|
||||||
pub root_path: Option<String>,
|
pub root_path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,17 +44,71 @@ impl State {
|
|||||||
State {
|
State {
|
||||||
stacks: Vec::new(),
|
stacks: Vec::new(),
|
||||||
stack_index: None,
|
stack_index: None,
|
||||||
image_index: None,
|
focus_image_index: None,
|
||||||
file_name: None,
|
file_name: None,
|
||||||
root_path: None,
|
root_path: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn set_image_index(&mut self, image_index: Option<usize>) {
|
|
||||||
self.image_index = image_index;
|
pub fn get_focus_image_index(&self) -> Option<usize> {
|
||||||
|
return self.focus_image_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, msg: &Message) {
|
||||||
|
match msg {
|
||||||
|
Message::OpenFile(file) => {
|
||||||
|
let filename = file.path().expect("Couldn't get file path");
|
||||||
|
let contents = fs::read_to_string(filename.clone())
|
||||||
|
.expect("Something went wrong reading the file");
|
||||||
|
//eprintln!("{}", contents);
|
||||||
|
|
||||||
|
let new_dataset: Vec<AnnotationZStack> = serde_json::from_str(&contents).unwrap();
|
||||||
|
self.replace_foucs_stacks(new_dataset);
|
||||||
|
self.file_name = filename.clone().as_path().file_name().map(|x| {
|
||||||
|
x.to_str()
|
||||||
|
.expect("failed to convert filname to str")
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
self.root_path = filename.clone().as_path().parent().map(|x| {
|
||||||
|
x.to_str()
|
||||||
|
.expect("failed to convert filname to str")
|
||||||
|
.to_string()
|
||||||
|
});
|
||||||
|
|
||||||
|
match (self.root_path.clone(), self.file_name.clone()) {
|
||||||
|
(Some(root_path), Some(file_name)) => {
|
||||||
|
let path = Path::new(&root_path).join(Path::new(&file_name));
|
||||||
|
eprintln!("{:?}", path);
|
||||||
|
}
|
||||||
|
(_, _) => {
|
||||||
|
eprintln!("Path not properly set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::NextImage => {
|
||||||
|
self.skip();
|
||||||
|
}
|
||||||
|
Message::PreviousImage => {
|
||||||
|
self.previous();
|
||||||
|
}
|
||||||
|
Message::MarkFocus => {
|
||||||
|
self.mark_focus();
|
||||||
|
self.save();
|
||||||
|
self.skip();
|
||||||
|
}
|
||||||
|
Message::FocusLevelChange(lvl) => {
|
||||||
|
self.set_focus_image_index(Some(*lvl));
|
||||||
|
}
|
||||||
|
Message::UI(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_focus_image_index(&mut self, image_index: Option<usize>) {
|
||||||
|
self.focus_image_index = image_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_annotation_image(&self) -> Option<AnnotationImage> {
|
pub fn get_current_annotation_image(&self) -> Option<AnnotationImage> {
|
||||||
match self.image_index {
|
match self.focus_image_index {
|
||||||
Some(image_index) => {
|
Some(image_index) => {
|
||||||
let stack = self.get_current_focus_stack();
|
let stack = self.get_current_focus_stack();
|
||||||
match stack {
|
match stack {
|
||||||
@@ -56,7 +134,7 @@ impl State {
|
|||||||
|
|
||||||
if let Some(z_stack) = self.stacks.first() {
|
if let Some(z_stack) = self.stacks.first() {
|
||||||
self.stack_index = Some(0);
|
self.stack_index = Some(0);
|
||||||
self.image_index = if let Some(_) = z_stack.images.first() {
|
self.focus_image_index = if let Some(_) = z_stack.images.first() {
|
||||||
Some(0)
|
Some(0)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@@ -87,9 +165,9 @@ impl State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn mark_focus(&mut self) {
|
pub fn mark_focus(&mut self) {
|
||||||
match (self.stack_index, self.image_index) {
|
match (self.stack_index, self.focus_image_index) {
|
||||||
(Some(stack_index), Some(_)) => {
|
(Some(stack_index), Some(_)) => {
|
||||||
self.stacks[stack_index].best_index = self.image_index;
|
self.stacks[stack_index].best_index = self.focus_image_index;
|
||||||
}
|
}
|
||||||
(_, _) => {}
|
(_, _) => {}
|
||||||
}
|
}
|
||||||
@@ -99,16 +177,24 @@ impl State {
|
|||||||
match (self.root_path.clone(), self.file_name.clone()) {
|
match (self.root_path.clone(), self.file_name.clone()) {
|
||||||
(Some(root_path), Some(file_name)) => {
|
(Some(root_path), Some(file_name)) => {
|
||||||
let path = Path::new(&root_path).join(Path::new(&file_name));
|
let path = Path::new(&root_path).join(Path::new(&file_name));
|
||||||
match File::create(path) {
|
match fs::File::create(path) {
|
||||||
Ok(mut file) => {
|
Ok(mut file) => {
|
||||||
|
use std::time::Instant;
|
||||||
|
let now = Instant::now();
|
||||||
let contents =
|
let contents =
|
||||||
serde_json::to_string(&self.stacks).expect("Could not serialize data.");
|
serde_json::to_string(&self.stacks).expect("Could not serialize data.");
|
||||||
|
let elapsed = now.elapsed();
|
||||||
|
println!("Elapsed: {:.2?}", elapsed);
|
||||||
|
|
||||||
|
let now = Instant::now();
|
||||||
match file.write(contents.as_bytes()) {
|
match file.write(contents.as_bytes()) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("an error occured while saving: {}", e.to_string());
|
eprintln!("an error occured while saving: {}", e.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let elapsed = now.elapsed();
|
||||||
|
println!("Elapsed: {:.2?}", elapsed);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e.to_string());
|
eprintln!("{}", e.to_string());
|
||||||
|
|||||||
104
src/ui/mod.rs
104
src/ui/mod.rs
@@ -2,14 +2,21 @@ use std::{path::Path, sync::Arc};
|
|||||||
|
|
||||||
use adw::{Application, ApplicationWindow, HeaderBar, SplitButton};
|
use adw::{Application, ApplicationWindow, HeaderBar, SplitButton};
|
||||||
use gtk::{
|
use gtk::{
|
||||||
traits::{BoxExt, GridExt, GtkApplicationExt, RangeExt, ScaleExt, WidgetExt},
|
glib::Sender,
|
||||||
ActionBar, AspectFrame, Box, Button, Grid, Image, Orientation, PositionType, Scale, Separator,
|
traits::{
|
||||||
ToggleButton,
|
BoxExt, ButtonExt, DialogExt, FileChooserExt, GridExt, GtkApplicationExt, GtkWindowExt,
|
||||||
|
RangeExt, ScaleExt, ToggleButtonExt, WidgetExt,
|
||||||
|
},
|
||||||
|
ActionBar, AspectFrame, Box, Button, FileChooserAction, FileChooserDialog, FileFilter, Grid,
|
||||||
|
Image, Orientation, PositionType, ResponseType, Scale, Separator, ToggleButton,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::{MARGIN_LEFT, MARGIN_RIGHT_SCALE_ADDITIONAL, MARGIN_TOP, TOGGLE_NEIGHBOURS_TEXT},
|
constants::{
|
||||||
state::State,
|
MARGIN_LEFT, MARGIN_RIGHT_SCALE_ADDITIONAL, MARGIN_TOP, SCALE_STEP, TOGGLE_NEIGHBOURS_TEXT,
|
||||||
|
TOGGLE_NEIGHBOURS_TEXT_TOGGLED,
|
||||||
|
},
|
||||||
|
state::{Message, State, UIMessage},
|
||||||
AnnotationImage, MARGIN_BOTTOM,
|
AnnotationImage, MARGIN_BOTTOM,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,15 +31,16 @@ pub struct ImageUI {
|
|||||||
pub focus_neighbours_grid: Arc<Grid>,
|
pub focus_neighbours_grid: Arc<Grid>,
|
||||||
pub focus_neighbours_aspect_frame: Arc<AspectFrame>,
|
pub focus_neighbours_aspect_frame: Arc<AspectFrame>,
|
||||||
|
|
||||||
pub neighbour_toggle_button: Arc<ToggleButton>,
|
pub neighbour_toggle_button: ToggleButton,
|
||||||
pub open_button: Arc<SplitButton>,
|
pub open_button: Arc<SplitButton>,
|
||||||
pub back_button: Arc<Button>,
|
pub back_button: Arc<Button>,
|
||||||
pub skip_button: Arc<Button>,
|
pub skip_button: Arc<Button>,
|
||||||
pub focus_button: Arc<Button>,
|
pub focus_button: Arc<Button>,
|
||||||
|
pub sender: Sender<Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImageUI {
|
impl ImageUI {
|
||||||
pub fn new(app: &Application) -> ImageUI {
|
pub fn new(app: &Application, sender: Sender<Message>) -> ImageUI {
|
||||||
let mut builder = ImageUIBuilder::default();
|
let mut builder = ImageUIBuilder::default();
|
||||||
let application_vertical_widget = Arc::new(Box::new(Orientation::Vertical, 0));
|
let application_vertical_widget = Arc::new(Box::new(Orientation::Vertical, 0));
|
||||||
|
|
||||||
@@ -48,8 +56,8 @@ impl ImageUI {
|
|||||||
|
|
||||||
builder
|
builder
|
||||||
.application_vertical_widget(application_vertical_widget.clone())
|
.application_vertical_widget(application_vertical_widget.clone())
|
||||||
.window(window);
|
.window(window)
|
||||||
|
.sender(sender);
|
||||||
// TODO: move into builder
|
// TODO: move into builder
|
||||||
ImageUI::build_header(&mut builder, application_vertical_widget.clone());
|
ImageUI::build_header(&mut builder, application_vertical_widget.clone());
|
||||||
ImageUI::build_center(&mut builder, application_vertical_widget.clone());
|
ImageUI::build_center(&mut builder, application_vertical_widget.clone());
|
||||||
@@ -165,12 +173,10 @@ impl ImageUI {
|
|||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let neighbour_toggle_button = Arc::new(
|
let neighbour_toggle_button = ToggleButton::builder()
|
||||||
ToggleButton::builder()
|
|
||||||
.label(TOGGLE_NEIGHBOURS_TEXT)
|
.label(TOGGLE_NEIGHBOURS_TEXT)
|
||||||
.width_request(158)
|
.width_request(158)
|
||||||
.build(),
|
.build();
|
||||||
);
|
|
||||||
|
|
||||||
let focus_skip_link_widget = Box::builder()
|
let focus_skip_link_widget = Box::builder()
|
||||||
.css_classes(vec!["linked".to_string()])
|
.css_classes(vec!["linked".to_string()])
|
||||||
@@ -179,7 +185,7 @@ impl ImageUI {
|
|||||||
focus_skip_link_widget.append(skip_button.as_ref());
|
focus_skip_link_widget.append(skip_button.as_ref());
|
||||||
focus_skip_link_widget.append(focus_button.as_ref());
|
focus_skip_link_widget.append(focus_button.as_ref());
|
||||||
|
|
||||||
bottom_toolbar.pack_start(neighbour_toggle_button.as_ref());
|
bottom_toolbar.pack_start(&neighbour_toggle_button);
|
||||||
bottom_toolbar.pack_end(&focus_skip_link_widget);
|
bottom_toolbar.pack_end(&focus_skip_link_widget);
|
||||||
|
|
||||||
application_vertical_widget.append(&bottom_toolbar);
|
application_vertical_widget.append(&bottom_toolbar);
|
||||||
@@ -195,7 +201,67 @@ impl ImageUI {
|
|||||||
self.window.show();
|
self.window.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&self, state: &State) {
|
pub fn refresh(&self, msg: &Message, state: &State) {
|
||||||
|
match msg {
|
||||||
|
Message::UI(UIMessage::ToggleGrid) => {
|
||||||
|
let curent_state = self.neighbour_toggle_button.is_active();
|
||||||
|
self.neighbour_toggle_button.set_active(!curent_state)
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::IncrementFocus) => {
|
||||||
|
self.focus_scale
|
||||||
|
.set_value(self.focus_scale.value() + SCALE_STEP);
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::DecrementFocus) => {
|
||||||
|
self.focus_scale
|
||||||
|
.set_value(self.focus_scale.value() - SCALE_STEP);
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::ShowGrid(true)) => {
|
||||||
|
self.focus_neighbours_aspect_frame
|
||||||
|
.set_child(Some(self.focus_neighbours_grid.as_ref()));
|
||||||
|
self.neighbour_toggle_button
|
||||||
|
.set_label(TOGGLE_NEIGHBOURS_TEXT_TOGGLED);
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::ShowGrid(false)) => {
|
||||||
|
self.focus_neighbours_aspect_frame
|
||||||
|
.set_child(Some(self.individual.as_ref()));
|
||||||
|
self.neighbour_toggle_button
|
||||||
|
.set_label(TOGGLE_NEIGHBOURS_TEXT);
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::OpenFileChooser) => {
|
||||||
|
let file_chooser_action = FileChooserAction::Open;
|
||||||
|
let buttons = [("Open", ResponseType::Ok), ("Cancel", ResponseType::Cancel)];
|
||||||
|
let filter = FileFilter::new();
|
||||||
|
filter.add_pattern(r"*.json");
|
||||||
|
|
||||||
|
let file_chooser = FileChooserDialog::new(
|
||||||
|
Some("Chose a data file!"),
|
||||||
|
Some(self.window.as_ref()),
|
||||||
|
file_chooser_action,
|
||||||
|
&buttons,
|
||||||
|
);
|
||||||
|
file_chooser.set_select_multiple(false);
|
||||||
|
file_chooser.set_filter(&filter);
|
||||||
|
|
||||||
|
let _sender = self.sender.clone();
|
||||||
|
file_chooser.connect_response(
|
||||||
|
move |dialog: &FileChooserDialog, response: ResponseType| {
|
||||||
|
if response == ResponseType::Ok {
|
||||||
|
let file = dialog.file().expect("Couldn't get file");
|
||||||
|
eprintln!("Open");
|
||||||
|
_sender.send(Message::OpenFile(file)).unwrap();
|
||||||
|
}
|
||||||
|
dialog.close();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
file_chooser.show();
|
||||||
|
}
|
||||||
|
Message::UI(UIMessage::RefreshImages)
|
||||||
|
| Message::NextImage
|
||||||
|
| Message::PreviousImage
|
||||||
|
| Message::MarkFocus
|
||||||
|
| Message::FocusLevelChange(_)
|
||||||
|
| Message::OpenFile(_) => {
|
||||||
match (
|
match (
|
||||||
state.get_current_annotation_image(),
|
state.get_current_annotation_image(),
|
||||||
state.root_path.clone(),
|
state.root_path.clone(),
|
||||||
@@ -203,10 +269,14 @@ impl ImageUI {
|
|||||||
(Some(annotation_image), Some(base_path)) => {
|
(Some(annotation_image), Some(base_path)) => {
|
||||||
self.update_image(&annotation_image, base_path)
|
self.update_image(&annotation_image, base_path)
|
||||||
}
|
}
|
||||||
(_, _) => {}
|
(_, _) => {
|
||||||
|
// TODO: write error message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.update_focus_scale(&state);
|
self.update_focus_scale(&state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
fn update_image(&self, annotation_image: &AnnotationImage, base_path: String) {
|
fn update_image(&self, annotation_image: &AnnotationImage, base_path: String) {
|
||||||
self.individual.set_from_file(Some(
|
self.individual.set_from_file(Some(
|
||||||
Path::new(&base_path).join(Path::new(&annotation_image.image_path)),
|
Path::new(&base_path).join(Path::new(&annotation_image.image_path)),
|
||||||
@@ -239,7 +309,7 @@ impl ImageUI {
|
|||||||
.set_margin_end(MARGIN_RIGHT_SCALE_ADDITIONAL);
|
.set_margin_end(MARGIN_RIGHT_SCALE_ADDITIONAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(current_value) = state.image_index {
|
if let Some(current_value) = state.get_focus_image_index() {
|
||||||
self.focus_scale.set_value(current_value as f64);
|
self.focus_scale.set_value(current_value as f64);
|
||||||
} else {
|
} else {
|
||||||
self.focus_scale.set_value(f64::floor(max / 2.0));
|
self.focus_scale.set_value(f64::floor(max / 2.0));
|
||||||
|
|||||||
Reference in New Issue
Block a user