I am trying to implement my own image viewer using eframe/egui in Rust. However I can't seem to get the loading and displaying of images using a file path to work.
I want the programm to work something like this:
Select a directory
Get every file from the directory
Display the newest image
Use buttons to click/loop through the images
Currently I am stuck at point 3 XD
fn main() -> Result<(), eframe::Error>{
env_logger::init();
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
..Default::default()
};
eframe::run_native(
"Image Viewer",
options,
Box::new(
|cc| {
egui_extras::install_image_loaders(&cc.egui_ctx);
Box::<ImageViewer>::default()
}
)
)
}
#[derive(Default)]
struct ImageViewer {
directory_path: Option<String>,
images: Vec<String>,
has_images: bool,
current_image: i32
}
impl eframe::App for ImageViewer {
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.vertical_centered_justified(|ui| {
ui.horizontal_top(|ui| {
let path_label = ui.label("Path:");
let window_width = ui.available_width();
if let Some(directory_path) = &self.directory_path {
if ui.add_sized(
[window_width, 10.0],
egui::Button::new(directory_path)
)
.labelled_by(path_label.id)
.clicked() {
if let Some(path) = FileDialog::new().set_directory("/").pick_folder() {
self.directory_path = Some(path.display().to_string());
self.images = get_image_paths_from_directory(path);
self.has_images = self.images.len() > 0;
self.current_image = 0;
}
}
} else {
if ui.add_sized(
[window_width, 10.0],
egui::Button::new("No image directory selected")
)
.labelled_by(path_label.id)
.clicked() {
if let Some(path) = FileDialog::new().set_directory("/").pick_folder() {
self.directory_path = Some(path.display().to_string());
self.images = get_image_paths_from_directory(path);
self.has_images = self.images.len() > 0;
self.current_image = 0;
}
};
}
});
ui.vertical_centered_justified(|ui| {
if self.has_images {
// Load and display image
} else {
// Show placeholder
}
})
})
});
}
}
// Gets the file paths from a given PathBuf
fn get_image_paths_from_directory(path_buf: PathBuf) -> Vec<String> {
path_buf
.read_dir()
.unwrap()
.map(|entry| {
let entry = entry.unwrap();
let entry_path = entry.path();
let file_name = entry_path.as_os_str();
let file_name_as_str = file_name.to_str().unwrap();
let file_name_as_string = String::from(file_name_as_str);
file_name_as_string
})
.collect::<Vec<String>>()
}
I know that you can implement images like this
ui.add(egui::Image::new(egui::include_image!(image_path)));
However you have to declare the image_path before running the programm and I can't seem to find a way to change the path mid execution (or if that is even possible). Also setting the varibable beforehand would not serve the purpose of selecting a directory and browsing its images.
I also tried some things converting the image into bytes and tied loading it that way, without positive results however.
Just pass an URI file:///path/to/your/file.png
to your Image::new
call, it takes anything that implements Into<ImageSource>
and there is an implementation of From<String> for ImageSource
that treats the String
as an URI:
let file_path = "/path/to/your/file.png";
let image = Image::new(format!("file://{file_path}"));
The examples on Ui::image
do cover that case and are be preferable to a manual ui.add(image)
:
ui.image("file://assets/ferris.png");