Similar images and proper tool type
This commit is contained in:
parent
9225025157
commit
b67a98f8ca
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3316,6 +3316,7 @@ dependencies = [
|
||||||
"czkawka_core",
|
"czkawka_core",
|
||||||
"handsome_logger",
|
"handsome_logger",
|
||||||
"home",
|
"home",
|
||||||
|
"humansize",
|
||||||
"log",
|
"log",
|
||||||
"open",
|
"open",
|
||||||
"rand",
|
"rand",
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
|
|
||||||
### Core
|
### Core
|
||||||
- Using normal crossbeam channels instead of asyncio tokio channel - [#1102](https://github.com/qarmin/czkawka/pull/1102)
|
- Using normal crossbeam channels instead of asyncio tokio channel - [#1102](https://github.com/qarmin/czkawka/pull/1102)
|
||||||
|
- Fixed tool type when using progress of empty directories
|
||||||
|
|
||||||
## Version 6.1.0 - 15.10.2023r
|
## Version 6.1.0 - 15.10.2023r
|
||||||
- BREAKING CHANGE - Changed cache saving method, deduplicated, optimized and simplified procedure(all files needs to be hashed again) - [#1072](https://github.com/qarmin/czkawka/pull/1072), [#1086](https://github.com/qarmin/czkawka/pull/1086)
|
- BREAKING CHANGE - Changed cache saving method, deduplicated, optimized and simplified procedure(all files needs to be hashed again) - [#1072](https://github.com/qarmin/czkawka/pull/1072), [#1086](https://github.com/qarmin/czkawka/pull/1086)
|
||||||
|
|
|
@ -220,6 +220,7 @@ impl BadExtensions {
|
||||||
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
||||||
.excluded_items(self.common_data.excluded_items.clone())
|
.excluded_items(self.common_data.excluded_items.clone())
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
|
|
@ -467,6 +467,7 @@ pub fn prepare_thread_handler_common(
|
||||||
checking_method: CheckingMethod,
|
checking_method: CheckingMethod,
|
||||||
tool_type: ToolType,
|
tool_type: ToolType,
|
||||||
) -> (JoinHandle<()>, Arc<AtomicBool>, Arc<AtomicUsize>, AtomicBool) {
|
) -> (JoinHandle<()>, Arc<AtomicBool>, Arc<AtomicUsize>, AtomicBool) {
|
||||||
|
assert_ne!(tool_type, ToolType::None, "ToolType::None should not exist");
|
||||||
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
let progress_thread_run = Arc::new(AtomicBool::new(true));
|
||||||
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
let atomic_counter = Arc::new(AtomicUsize::new(0));
|
||||||
let check_was_stopped = AtomicBool::new(false);
|
let check_was_stopped = AtomicBool::new(false);
|
||||||
|
|
|
@ -187,7 +187,7 @@ impl<'a, 'b> DirTraversalBuilder<'a, 'b, ()> {
|
||||||
directories: None,
|
directories: None,
|
||||||
allowed_extensions: None,
|
allowed_extensions: None,
|
||||||
excluded_items: None,
|
excluded_items: None,
|
||||||
tool_type: ToolType::BadExtensions,
|
tool_type: ToolType::None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,6 +341,8 @@ where
|
||||||
{
|
{
|
||||||
#[fun_time(message = "run(collecting files/dirs)", level = "debug")]
|
#[fun_time(message = "run(collecting files/dirs)", level = "debug")]
|
||||||
pub fn run(self) -> DirTraversalResult<T> {
|
pub fn run(self) -> DirTraversalResult<T> {
|
||||||
|
assert!(self.tool_type != ToolType::None, "Tool type cannot be None");
|
||||||
|
|
||||||
let mut all_warnings = vec![];
|
let mut all_warnings = vec![];
|
||||||
let mut grouped_file_entries: BTreeMap<T, Vec<FileEntry>> = BTreeMap::new();
|
let mut grouped_file_entries: BTreeMap<T, Vec<FileEntry>> = BTreeMap::new();
|
||||||
let mut folder_entries: BTreeMap<PathBuf, FolderEntry> = BTreeMap::new();
|
let mut folder_entries: BTreeMap<PathBuf, FolderEntry> = BTreeMap::new();
|
||||||
|
|
|
@ -169,6 +169,7 @@ impl DuplicateFinder {
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
.minimal_file_size(self.common_data.minimal_file_size)
|
.minimal_file_size(self.common_data.minimal_file_size)
|
||||||
.maximal_file_size(self.common_data.maximal_file_size)
|
.maximal_file_size(self.common_data.maximal_file_size)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
@ -244,6 +245,7 @@ impl DuplicateFinder {
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
.minimal_file_size(self.common_data.minimal_file_size)
|
.minimal_file_size(self.common_data.minimal_file_size)
|
||||||
.maximal_file_size(self.common_data.maximal_file_size)
|
.maximal_file_size(self.common_data.maximal_file_size)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
@ -321,6 +323,7 @@ impl DuplicateFinder {
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
.minimal_file_size(self.common_data.minimal_file_size)
|
.minimal_file_size(self.common_data.minimal_file_size)
|
||||||
.maximal_file_size(self.common_data.maximal_file_size)
|
.maximal_file_size(self.common_data.maximal_file_size)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ impl EmptyFiles {
|
||||||
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
||||||
.excluded_items(self.common_data.excluded_items.clone())
|
.excluded_items(self.common_data.excluded_items.clone())
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ impl EmptyFolder {
|
||||||
.excluded_items(self.common_data.excluded_items.clone())
|
.excluded_items(self.common_data.excluded_items.clone())
|
||||||
.collect(Collect::EmptyFolders)
|
.collect(Collect::EmptyFolders)
|
||||||
.max_stage(0)
|
.max_stage(0)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,7 @@ impl InvalidSymlinks {
|
||||||
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
||||||
.excluded_items(self.common_data.excluded_items.clone())
|
.excluded_items(self.common_data.excluded_items.clone())
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,7 @@ impl SameMusic {
|
||||||
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
.allowed_extensions(self.common_data.allowed_extensions.clone())
|
||||||
.excluded_items(self.common_data.excluded_items.clone())
|
.excluded_items(self.common_data.excluded_items.clone())
|
||||||
.recursive_search(self.common_data.recursive_search)
|
.recursive_search(self.common_data.recursive_search)
|
||||||
|
.tool_type(self.common_data.tool_type)
|
||||||
.max_stage(max_stage)
|
.max_stage(max_stage)
|
||||||
.build()
|
.build()
|
||||||
.run();
|
.run();
|
||||||
|
|
|
@ -94,7 +94,7 @@ pub struct SimilarImages {
|
||||||
// Hashmap with image hashes and Vector with names of files
|
// Hashmap with image hashes and Vector with names of files
|
||||||
similarity: u32,
|
similarity: u32,
|
||||||
images_to_check: BTreeMap<String, FileEntry>,
|
images_to_check: BTreeMap<String, FileEntry>,
|
||||||
hash_size: u8,
|
pub hash_size: u8, // TODO to remove pub, this is needeed by new gui, because there is no way to check what exactly was seelected
|
||||||
hash_alg: HashAlg,
|
hash_alg: HashAlg,
|
||||||
image_filter: FilterType,
|
image_filter: FilterType,
|
||||||
exclude_images_with_same_size: bool,
|
exclude_images_with_same_size: bool,
|
||||||
|
|
|
@ -31,6 +31,7 @@ home = "0.5.5"
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
humansize = "2.1.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
slint-build = "1.3.0"
|
slint-build = "1.3.0"
|
||||||
|
|
21
krokiet/LICENSE_MIT_CODE
Normal file
21
krokiet/LICENSE_MIT_CODE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2020-2023 Rafał Mikrut
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -73,14 +73,13 @@ should print something like
|
||||||
Slint: Build config: debug; Backend: software
|
Slint: Build config: debug; Backend: software
|
||||||
```
|
```
|
||||||
|
|
||||||
## Dark/Different theme
|
## Different theme
|
||||||
|
|
||||||
App was created with white fluent theme by default, but is easily possible to use dark theme by setting `SLINT_STYLE`
|
App was created with fluent theme in mind, but is possible to use dark theme by setting `SLINT_STYLE` environment
|
||||||
environment variable to `fluent-dark` during compilation
|
variable to `fluent-dark` during compilation e.g.
|
||||||
e.g.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
SLINT_STYLE=fluent-dark cargo run -- --path .
|
SLINT_STYLE=fluent-light cargo run -- --path .
|
||||||
```
|
```
|
||||||
|
|
||||||
Slint supports also other themes, but they are not officially supported by this app and may be broken.
|
Slint supports also other themes, but they are not officially supported by this app and may be broken.
|
||||||
|
@ -97,7 +96,8 @@ SLINT_STYLE=material-dark cargo run -- --path .
|
||||||
- Suggesting possible design changes in the gui - of course, they should be possible to be simply implemented in the
|
- Suggesting possible design changes in the gui - of course, they should be possible to be simply implemented in the
|
||||||
slint keeping in mind the performance aspect as well
|
slint keeping in mind the performance aspect as well
|
||||||
- Modifying user interface - gui is written in simple language similar to qml, that can be modified in vscode/web with
|
- Modifying user interface - gui is written in simple language similar to qml, that can be modified in vscode/web with
|
||||||
live preview - [slint live preview example](https://slint.dev/releases/1.3.0/editor/?load_demo=examples/printerdemo/ui/printerdemo.slint)
|
live
|
||||||
|
preview - [slint live preview example](https://slint.dev/releases/1.3.0/editor/?load_demo=examples/printerdemo/ui/printerdemo.slint)
|
||||||
- Improving libraries used by Krokiet e.g. czkawka_core, image-rs etc.
|
- Improving libraries used by Krokiet e.g. czkawka_core, image-rs etc.
|
||||||
- Improving app rust code
|
- Improving app rust code
|
||||||
|
|
||||||
|
@ -120,6 +120,8 @@ SLINT_STYLE=material-dark cargo run -- --path .
|
||||||
- proper popup windows - slint not handle them properly
|
- proper popup windows - slint not handle them properly
|
||||||
- logo
|
- logo
|
||||||
- about window
|
- about window
|
||||||
|
- reference folders
|
||||||
|
- translations(problem is only with interface, messages like "Checking {x} file" can be easily translated from rust side)
|
||||||
|
|
||||||
## Why Slint?
|
## Why Slint?
|
||||||
|
|
||||||
|
@ -145,10 +147,13 @@ errors.
|
||||||
|
|
||||||
So only Slint left with its cons and pros.
|
So only Slint left with its cons and pros.
|
||||||
|
|
||||||
|
## License
|
||||||
|
Code is licensed under MIT license but entire project is licensed under GPL-3.0 license, due Slint license restrictions.
|
||||||
|
|
||||||
## Name
|
## Name
|
||||||
|
|
||||||
Why Krokiet(eng. Croquette)?
|
Why Krokiet(eng. Croquette)?
|
||||||
Because I like croquettes(Polish version), the ones with meat, mushrooms wrapped in breadcrumbs... it makes my mouth
|
Because I like croquettes(Polish version), the ones with meat, mushrooms wrapped in breadcrumbs... it makes my mouth
|
||||||
water.
|
water.
|
||||||
I considered also other dishes which I like to eat like pierogi, żurek, pączek, schabowy or zapiekanka.
|
I considered also other dishes which I like to eat like pierogi, żurek, pączek, schabowy or zapiekanka.
|
||||||
This name should be a lot of easier to remember than Czkawka.
|
This name should be a lot of easier to remember than czkawka or szyszka.
|
|
@ -1,7 +1,7 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
if env::var("SLINT_STYLE").is_err() || env::var("SLINT_STYLE") == Ok("".into()) {
|
if env::var("SLINT_STYLE").is_err() || env::var("SLINT_STYLE") == Ok(String::new()) {
|
||||||
slint_build::compile_with_config("ui/main_window.slint", slint_build::CompilerConfiguration::new().with_style("fluent-dark".into())).unwrap();
|
slint_build::compile_with_config("ui/main_window.slint", slint_build::CompilerConfiguration::new().with_style("fluent-dark".into())).unwrap();
|
||||||
} else {
|
} else {
|
||||||
slint_build::compile("ui/main_window.slint").unwrap();
|
slint_build::compile("ui/main_window.slint").unwrap();
|
||||||
|
|
|
@ -23,3 +23,7 @@ pub fn create_string_standard_list_view_from_pathbuf(items: &[PathBuf]) -> Model
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
ModelRc::new(VecModel::from(new_folders_standard_list_view))
|
ModelRc::new(VecModel::from(new_folders_standard_list_view))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_vec_model_from_vec_string(items: Vec<String>) -> VecModel<SharedString> {
|
||||||
|
VecModel::from(items.into_iter().map(SharedString::from).collect::<Vec<_>>())
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{MainWindow, ProgressToSend};
|
use crate::{MainWindow, ProgressToSend};
|
||||||
|
|
||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use czkawka_core::common_dir_traversal::ProgressData;
|
use czkawka_core::common_dir_traversal::{ProgressData, ToolType};
|
||||||
use slint::{ComponentHandle, SharedString};
|
use slint::{ComponentHandle, SharedString};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
|
@ -14,29 +14,80 @@ pub fn connect_progress_gathering(app: &MainWindow, progress_receiver: Receiver<
|
||||||
};
|
};
|
||||||
|
|
||||||
a.upgrade_in_event_loop(move |app| {
|
a.upgrade_in_event_loop(move |app| {
|
||||||
let (all_stages, current_stage) = common_get_data(&progress_data);
|
let to_send;
|
||||||
let to_send = ProgressToSend {
|
match progress_data.tool_type {
|
||||||
all_progress: (all_stages * 100.0) as i32,
|
ToolType::EmptyFiles => {
|
||||||
current_progress: (current_stage * 100.0) as i32,
|
let (all_progress, current_progress) = no_current_stage_get_data(&progress_data);
|
||||||
step_name: SharedString::from(format!("Checked {} folders", progress_data.entries_checked)),
|
to_send = ProgressToSend {
|
||||||
};
|
all_progress,
|
||||||
|
current_progress,
|
||||||
|
step_name: SharedString::from(format!("Checked {} files", progress_data.entries_checked)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ToolType::EmptyFolders => {
|
||||||
|
let (all_progress, current_progress) = no_current_stage_get_data(&progress_data);
|
||||||
|
to_send = ProgressToSend {
|
||||||
|
all_progress,
|
||||||
|
current_progress,
|
||||||
|
step_name: SharedString::from(format!("Checked {} folders", progress_data.entries_checked)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ToolType::SimilarImages => {
|
||||||
|
let step_name;
|
||||||
|
let all_progress;
|
||||||
|
let current_progress;
|
||||||
|
match progress_data.current_stage {
|
||||||
|
0 => {
|
||||||
|
(all_progress, current_progress) = no_current_stage_get_data(&progress_data);
|
||||||
|
step_name = format!("Scanning {} file", progress_data.entries_checked);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
(all_progress, current_progress) = common_get_data(&progress_data);
|
||||||
|
step_name = format!("Hashing {}/{} image", progress_data.entries_checked, progress_data.entries_to_check);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
(all_progress, current_progress) = common_get_data(&progress_data);
|
||||||
|
step_name = format!("Comparing {}/{} image hash", progress_data.entries_checked, progress_data.entries_to_check);
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
to_send = ProgressToSend {
|
||||||
|
all_progress,
|
||||||
|
current_progress,
|
||||||
|
step_name: SharedString::from(step_name),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Invalid tool type {:?}", progress_data.tool_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
app.set_progress_datas(to_send);
|
app.set_progress_datas(to_send);
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn common_get_data(item: &ProgressData) -> (f64, f64) {
|
|
||||||
|
// Used when current stage not have enough data to show status, so we show only all_stages
|
||||||
|
// Happens if we searching files and we don't know how many files we need to check
|
||||||
|
fn no_current_stage_get_data(item: &ProgressData) -> (i32, i32) {
|
||||||
|
let all_stages = (item.current_stage as f64) / (item.max_stage + 1) as f64;
|
||||||
|
|
||||||
|
((all_stages * 100.0) as i32, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to calculate number of files to check and also to calculate current progress according to number of files to check and checked
|
||||||
|
fn common_get_data(item: &ProgressData) -> (i32, i32) {
|
||||||
if item.entries_to_check != 0 {
|
if item.entries_to_check != 0 {
|
||||||
let all_stages = (item.current_stage as f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64;
|
let all_stages = (item.current_stage as f64 + (item.entries_checked) as f64 / item.entries_to_check as f64) / (item.max_stage + 1) as f64;
|
||||||
let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages };
|
let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages };
|
||||||
|
|
||||||
let current_stage = (item.entries_checked) as f64 / item.entries_to_check as f64;
|
let current_stage = (item.entries_checked) as f64 / item.entries_to_check as f64;
|
||||||
let current_stage = if current_stage > 0.99 { 0.99 } else { current_stage };
|
let current_stage = if current_stage > 0.99 { 0.99 } else { current_stage };
|
||||||
(all_stages, current_stage)
|
((all_stages * 100.0) as i32, (current_stage * 100.0) as i32)
|
||||||
} else {
|
} else {
|
||||||
let all_stages = (item.current_stage as f64) / (item.max_stage + 1) as f64;
|
let all_stages = (item.current_stage as f64) / (item.max_stage + 1) as f64;
|
||||||
let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages };
|
let all_stages = if all_stages > 0.99 { 0.99 } else { all_stages };
|
||||||
(all_stages, 0f64)
|
((all_stages * 100.0) as i32, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::common::create_vec_model_from_vec_string;
|
||||||
use crate::settings::{collect_settings, SettingsCustom};
|
use crate::settings::{collect_settings, SettingsCustom};
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
use crate::{CurrentTab, MainListModel, MainWindow, ProgressToSend};
|
use crate::{CurrentTab, MainListModel, MainWindow, ProgressToSend};
|
||||||
|
@ -9,6 +10,10 @@ use czkawka_core::common_tool::CommonData;
|
||||||
use czkawka_core::common_traits::ResultEntry;
|
use czkawka_core::common_traits::ResultEntry;
|
||||||
use czkawka_core::empty_files::EmptyFiles;
|
use czkawka_core::empty_files::EmptyFiles;
|
||||||
use czkawka_core::empty_folder::EmptyFolder;
|
use czkawka_core::empty_folder::EmptyFolder;
|
||||||
|
use czkawka_core::similar_images;
|
||||||
|
use czkawka_core::similar_images::SimilarImages;
|
||||||
|
use humansize::format_size;
|
||||||
|
use humansize::BINARY;
|
||||||
use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak};
|
use slint::{ComponentHandle, ModelRc, SharedString, VecModel, Weak};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
@ -23,7 +28,7 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender<ProgressDat
|
||||||
|
|
||||||
app.set_progress_datas(ProgressToSend {
|
app.set_progress_datas(ProgressToSend {
|
||||||
all_progress: 0,
|
all_progress: 0,
|
||||||
current_progress: 0,
|
current_progress: -1,
|
||||||
step_name: SharedString::from(""),
|
step_name: SharedString::from(""),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -37,11 +42,67 @@ pub fn connect_scan_button(app: &MainWindow, progress_sender: Sender<ProgressDat
|
||||||
CurrentTab::EmptyFiles => {
|
CurrentTab::EmptyFiles => {
|
||||||
scan_empty_files(a, progress_sender, stop_receiver, custom_settings);
|
scan_empty_files(a, progress_sender, stop_receiver, custom_settings);
|
||||||
}
|
}
|
||||||
_ => panic!(),
|
CurrentTab::SimilarImages => {
|
||||||
|
scan_similar_images(a, progress_sender, stop_receiver, custom_settings);
|
||||||
|
} // _ => panic!(),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO handle referenced folders
|
||||||
|
fn scan_similar_images(a: Weak<MainWindow>, progress_sender: Sender<ProgressData>, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) {
|
||||||
|
thread::spawn(move || {
|
||||||
|
let mut finder = SimilarImages::new();
|
||||||
|
finder.set_included_directory(custom_settings.included_directories.clone());
|
||||||
|
finder.set_excluded_directory(custom_settings.excluded_directories.clone());
|
||||||
|
finder.find_similar_images(Some(&stop_receiver), Some(&progress_sender));
|
||||||
|
|
||||||
|
let mut vector = finder.get_similar_images().clone();
|
||||||
|
let messages = finder.get_text_messages().create_messages_text();
|
||||||
|
|
||||||
|
for vec_fe in &mut vector {
|
||||||
|
vec_fe.sort_unstable_by_key(|e| e.similarity);
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash_size = finder.hash_size;
|
||||||
|
|
||||||
|
a.upgrade_in_event_loop(move |app| {
|
||||||
|
let number_of_empty_files = vector.len();
|
||||||
|
let items = Rc::new(VecModel::default());
|
||||||
|
for vec_fe in vector {
|
||||||
|
items.push(MainListModel {
|
||||||
|
checked: false,
|
||||||
|
header_row: true,
|
||||||
|
selected_row: false,
|
||||||
|
val: ModelRc::new(VecModel::default()),
|
||||||
|
});
|
||||||
|
for fe in vec_fe {
|
||||||
|
let (directory, file) = split_path(fe.get_path());
|
||||||
|
let data_model = create_vec_model_from_vec_string(vec![
|
||||||
|
similar_images::get_string_from_similarity(&fe.similarity, hash_size),
|
||||||
|
format_size(fe.size, BINARY),
|
||||||
|
fe.dimensions.clone(),
|
||||||
|
file,
|
||||||
|
directory,
|
||||||
|
NaiveDateTime::from_timestamp_opt(fe.get_modified_date() as i64, 0).unwrap().to_string(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
let main = MainListModel {
|
||||||
|
checked: false,
|
||||||
|
header_row: false,
|
||||||
|
selected_row: false,
|
||||||
|
val: ModelRc::new(data_model),
|
||||||
|
};
|
||||||
|
items.push(main);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app.set_similar_images_model(items.into());
|
||||||
|
app.invoke_scan_ended(format!("Found {} similar images files", number_of_empty_files).into());
|
||||||
|
app.global::<Settings>().set_info_text(messages.into());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn scan_empty_files(a: Weak<MainWindow>, progress_sender: Sender<ProgressData>, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) {
|
fn scan_empty_files(a: Weak<MainWindow>, progress_sender: Sender<ProgressData>, stop_receiver: Receiver<()>, custom_settings: SettingsCustom) {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut finder = EmptyFiles::new();
|
let mut finder = EmptyFiles::new();
|
||||||
|
|
|
@ -97,6 +97,7 @@ component DirectoriesPanel {
|
||||||
component TextErrorsPanel inherits TextEdit {
|
component TextErrorsPanel inherits TextEdit {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
read-only: true;
|
read-only: true;
|
||||||
|
wrap: TextWrap.no-wrap;
|
||||||
text <=> Settings.info_text;
|
text <=> Settings.info_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,12 +39,12 @@ export component MainList {
|
||||||
|
|
||||||
if root.active-tab == CurrentTab.SimilarImages: SelectableTableView {
|
if root.active-tab == CurrentTab.SimilarImages: SelectableTableView {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
columns: ["Selection", "File Name", "Path"];
|
columns: ["Selection", "Similarity", "Size", "Dimensions", "File Name", "Path"];
|
||||||
last-column: "Modification Date";
|
last-column: "Modification Date";
|
||||||
column-sizes: [35px, 100px, 350px, 100px];
|
column-sizes: [35px, 80px, 80px, 80px, 350px, 100px, 100px];
|
||||||
values <=> similar-images-model;
|
values <=> similar-images-model;
|
||||||
parentPathIdx: 2;
|
parentPathIdx: 5;
|
||||||
fileNameIdx: 1;
|
fileNameIdx: 4;
|
||||||
item_opened(item) => {
|
item_opened(item) => {
|
||||||
item_opened(item)
|
item_opened(item)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ export component Progress {
|
||||||
VerticalLayout {
|
VerticalLayout {
|
||||||
spacing: 5px;
|
spacing: 5px;
|
||||||
Text {
|
Text {
|
||||||
|
visible: progress_datas.current-progress >= -0.001;
|
||||||
vertical-alignment: TextVerticalAlignment.center;
|
vertical-alignment: TextVerticalAlignment.center;
|
||||||
text: progress_datas.current-progress + "%";
|
text: progress_datas.current-progress + "%";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue