mirror of
https://github.com/qarmin/czkawka.git
synced 2025-09-07 01:56:09 +00:00
Resize all images, before showing them in gui (#1590)
This commit is contained in:
48
.github/workflows/mac.yml
vendored
48
.github/workflows/mac.yml
vendored
@@ -46,14 +46,14 @@ jobs:
|
||||
export LIBRARY_PATH=$LIBRARY_PATH:$(brew --prefix)/lib
|
||||
|
||||
cargo build --release
|
||||
mv target/release/czkawka_cli macos_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_gui macos_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mv target/release/krokiet macos_krokiet_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_cli mac_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_gui mac_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mv target/release/krokiet mac_krokiet_${{ env.ARCHNAME }}
|
||||
|
||||
cargo build --release --features "heif,libavif"
|
||||
mv target/release/czkawka_cli macos_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_gui macos_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/release/krokiet macos_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_cli mac_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/release/czkawka_gui mac_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/release/krokiet mac_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
|
||||
- name: Build Debug
|
||||
if: ${{ github.ref != 'refs/heads/master' }}
|
||||
@@ -67,26 +67,26 @@ jobs:
|
||||
export LIBRARY_PATH=$LIBRARY_PATH:$(brew --prefix)/lib
|
||||
|
||||
cargo build
|
||||
mv target/debug/czkawka_cli macos_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_gui macos_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mv target/debug/krokiet macos_krokiet_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_cli mac_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_gui mac_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mv target/debug/krokiet mac_krokiet_${{ env.ARCHNAME }}
|
||||
|
||||
cargo build --features "heif,libavif"
|
||||
mv target/debug/czkawka_cli macos_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_gui macos_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/debug/krokiet macos_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_cli mac_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/debug/czkawka_gui mac_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mv target/debug/krokiet mac_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
|
||||
- name: Store MacOS
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: all-${{ runner.os }}-${{ runner.arch }}-${{ env.VERS }}
|
||||
path: |
|
||||
macos_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_czkawka_cli_${{ env.ARCHNAME }}
|
||||
macos_czkawka_gui_${{ env.ARCHNAME }}
|
||||
macos_krokiet_${{ env.ARCHNAME }}
|
||||
mac_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mac_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mac_krokiet_${{ env.ARCHNAME }}
|
||||
|
||||
- name: Release
|
||||
if: ${{ github.ref == 'refs/heads/master' }}
|
||||
@@ -94,10 +94,10 @@ jobs:
|
||||
with:
|
||||
tag_name: "Nightly"
|
||||
files: |
|
||||
macos_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
macos_czkawka_cli_${{ env.ARCHNAME }}
|
||||
macos_czkawka_gui_${{ env.ARCHNAME }}
|
||||
macos_krokiet_${{ env.ARCHNAME }}
|
||||
mac_czkawka_cli_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_czkawka_gui_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_krokiet_heif_avif_${{ env.ARCHNAME }}
|
||||
mac_czkawka_cli_${{ env.ARCHNAME }}
|
||||
mac_czkawka_gui_${{ env.ARCHNAME }}
|
||||
mac_krokiet_${{ env.ARCHNAME }}
|
||||
token: ${{ secrets.PAT_REPOSITORY }}
|
||||
|
8
.github/workflows/windows.yml
vendored
8
.github/workflows/windows.yml
vendored
@@ -182,7 +182,9 @@ jobs:
|
||||
|
||||
- name: Prepare files to release
|
||||
run: |
|
||||
zip -r windows_czkawka_gui_gtk_412.zip ./package
|
||||
cd package
|
||||
zip -r ../windows_czkawka_gui_gtk_412.zip .
|
||||
cd ..
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
@@ -266,7 +268,9 @@ jobs:
|
||||
|
||||
- name: Prepare files to release
|
||||
run: |
|
||||
zip -r windows_czkawka_gui_gtk_46.zip ./package
|
||||
cd package
|
||||
zip -r ../windows_czkawka_gui_gtk_46.zip .
|
||||
cd ..
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
|
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -4041,6 +4041,7 @@ dependencies = [
|
||||
"crossbeam-channel",
|
||||
"czkawka_core",
|
||||
"dunce",
|
||||
"fast_image_resize",
|
||||
"fs_extra",
|
||||
"home",
|
||||
"humansize",
|
||||
@@ -4348,9 +4349,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lopdf"
|
||||
version = "0.36.0"
|
||||
version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59fa2559e99ba0f26a12458aabc754432c805bbb8cba516c427825a997af1fb7"
|
||||
checksum = "674a3504c1224247e00762afb90690991b673c461f6779565e055e91926a49da"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"bitflags 2.9.1",
|
||||
@@ -4359,6 +4360,7 @@ dependencies = [
|
||||
"ecb",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"getrandom 0.3.3",
|
||||
"indexmap",
|
||||
"itoa",
|
||||
"jiff",
|
||||
@@ -7337,15 +7339,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "trash"
|
||||
version = "5.2.2"
|
||||
version = "5.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22746c6b0c6d85d60a8f0d858f7057dfdf11297c132679f452ec908fba42b871"
|
||||
checksum = "65a334451012017a39758aa85a30827c13ac684245bf6b08249483c063f64ff3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"libc",
|
||||
"log",
|
||||
"objc2 0.5.2",
|
||||
"objc2-foundation 0.2.2",
|
||||
"objc2 0.6.2",
|
||||
"objc2-foundation 0.3.1",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"scopeguard",
|
||||
|
@@ -1,7 +1,6 @@
|
||||
## Version ?.?.? - ?
|
||||
### Release blockers:
|
||||
- Missing support for hardlinking/softlinking files in Krokiet
|
||||
- Unnecessary tokio dependency in rawler
|
||||
- crash when sorting by size, elements in empty folders
|
||||
|
||||
### Breaking changes
|
||||
#### Users
|
||||
@@ -51,6 +50,7 @@
|
||||
- Improved appearance of bottom directories panel - [#1569](https://github.com/qarmin/czkawka/pull/1569)
|
||||
- Some buttons, are disabled, when there is no files selected - [#1586](https://github.com/qarmin/czkawka/pull/1586)
|
||||
- Added info about the number of items selected to delete - [#1589](https://github.com/qarmin/czkawka/pull/1589)
|
||||
- Limit image preview to max 1024 width/height, to speedup preview loading and fixing crash in software renderer - [#1590](https://github.com/qarmin/czkawka/pull/1590)
|
||||
|
||||
### External
|
||||
- There is a new unofficial Tauri-based frontend for Czkawka - [Czkawka Tauri](https://github.com/shixinhuang99/czkawka-tauri)
|
||||
@@ -60,7 +60,6 @@
|
||||
- Compilation for 32-bit targets is now checked in CI
|
||||
- Czkawka binaries are now checked for reproducibility in CI
|
||||
|
||||
|
||||
### Prebuilt binaries
|
||||
- AppImage binaries are no longer provided due to random bugs (not present in other packaging formats) and minimal added value compared to prebuilt Linux binaries or Flatpak
|
||||
- HEIF Mac binaries are now provided
|
||||
|
56
README.md
56
README.md
@@ -54,32 +54,35 @@ two apps shouldn't be compared directly or be considered as an alternative to on
|
||||
In this comparison remember, that even if app have same features they may work different(e.g. one app may have more
|
||||
options to choose than other).
|
||||
|
||||
| | Czkawka | Krokiet | FSlint | DupeGuru | Bleachbit |
|
||||
|:------------------------:|:-----------:|:-----------:|:------:|:-----------------:|:-----------:|
|
||||
| Language | Rust | Rust | Python | Python/Obj-C | Python |
|
||||
| Framework base language | C | Rust | C | C/C++/Obj-C/Swift | C |
|
||||
| Framework | GTK 4 | Slint | PyGTK2 | Qt 5 (PyQt)/Cocoa | PyGTK3 |
|
||||
| OS | Lin,Mac,Win | Lin,Mac,Win | Lin | Lin,Mac,Win | Lin,Mac,Win |
|
||||
| Duplicate finder | ✔ | ✔ | ✔ | ✔ | |
|
||||
| Empty files | ✔ | ✔ | ✔ | | |
|
||||
| Empty folders | ✔ | ✔ | ✔ | | |
|
||||
| Temporary files | ✔ | ✔ | ✔ | | ✔ |
|
||||
| Big files | ✔ | ✔ | | | |
|
||||
| Similar images | ✔ | ✔ | | ✔ | |
|
||||
| Similar videos | ✔ | ✔ | | | |
|
||||
| Music duplicates(tags) | ✔ | ✔ | | ✔ | |
|
||||
| Invalid symlinks | ✔ | ✔ | ✔ | | |
|
||||
| Broken files | ✔ | ✔ | | | |
|
||||
| Names conflict | ✔ | ✔ | ✔ | | |
|
||||
| Invalid names/extensions | ✔ | ✔ | ✔ | | |
|
||||
| Installed packages | | | ✔ | | |
|
||||
| Bad ID | | | ✔ | | |
|
||||
| Non stripped binaries | | | ✔ | | |
|
||||
| Redundant whitespace | | | ✔ | | |
|
||||
| Overwriting files | | | ✔ | | ✔ |
|
||||
| Multiple languages | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| Cache support | ✔ | ✔ | | ✔ | |
|
||||
| In active development | Yes | Yes | No | Yes | Yes |
|
||||
| | Czkawka | Krokiet | FSlint | DupeGuru | Bleachbit |
|
||||
|:-------------------------:|:-----------:|:-----------:|:------:|:-----------------:|:-----------:|
|
||||
| Language | Rust | Rust | Python | Python/Obj-C | Python |
|
||||
| Framework base language | C | Rust | C | C/C++/Obj-C/Swift | C |
|
||||
| Framework | GTK 4 | Slint | PyGTK2 | Qt 5 (PyQt)/Cocoa | PyGTK3 |
|
||||
| OS | Lin,Mac,Win | Lin,Mac,Win | Lin | Lin,Mac,Win | Lin,Mac,Win |
|
||||
| Duplicate finder | ✔ | ✔ | ✔ | ✔ | |
|
||||
| Empty files | ✔ | ✔ | ✔ | | |
|
||||
| Empty folders | ✔ | ✔ | ✔ | | |
|
||||
| Temporary files | ✔ | ✔ | ✔ | | ✔ |
|
||||
| Big files | ✔ | ✔ | | | |
|
||||
| Similar images | ✔ | ✔ | | ✔ | |
|
||||
| Similar videos | ✔ | ✔ | | | |
|
||||
| Music duplicates(tags) | ✔ | ✔ | | ✔ | |
|
||||
| Music duplicates(content) | ✔ | ✔ | | | |
|
||||
| Invalid symlinks | ✔ | ✔ | ✔ | | |
|
||||
| Broken files | ✔ | ✔ | | | |
|
||||
| Invalid names/extensions | ✔ | ✔ | ✔ | | |
|
||||
| Names conflict | | | ✔ | | |
|
||||
| Installed packages | | | ✔ | | |
|
||||
| Bad ID | | | ✔ | | |
|
||||
| Non stripped binaries | | | ✔ | | |
|
||||
| Redundant whitespace | | | ✔ | | |
|
||||
| Overwriting files | | | ✔ | | ✔ |
|
||||
| Multiple languages | ✔ | ✔ | ✔ | ✔ | ✔ |
|
||||
| Cache support | ✔ | ✔ | | ✔ | |
|
||||
| In active development | Yes | Yes | No | No* | Yes |
|
||||
|
||||
* Last commit in 2024 and last version released in 2023
|
||||
|
||||
## Other apps
|
||||
|
||||
@@ -131,3 +134,4 @@ The Czkawka GTK GUI and CLI applications are licensed under the [MIT](https://mi
|
||||
|
||||
If you are using the app, I would appreciate a donation for its further development, which can be
|
||||
done [here](https://github.com/sponsors/qarmin).
|
||||
|
||||
|
@@ -1,50 +1,56 @@
|
||||
# Czkawka CLI
|
||||
|
||||
CLI frontend, allows to use Czkawka from terminal.
|
||||
CLI frontend that allows you to use Czkawka from the terminal.
|
||||
|
||||
## Requirements
|
||||
|
||||
Precompiled binaries should work without any additional dependencies with Linux(Ubuntu 20.04+), Windows(10+) and macOS(
|
||||
10.15+).
|
||||
Precompiled binaries should work without any additional dependencies on Linux (Ubuntu 22.04+), Windows (10+), and macOS (10.15+).
|
||||
|
||||
If you decide to compile the app, you probably will be able to run it on even older versions of OS, like Ubuntu 16.04 or
|
||||
Windows 7.
|
||||
On Linux, it is even possible (with eyra) to avoid libc entirely and use a fully static Rust binary, but alternatively you can use musl for this task.
|
||||
|
||||
On linux it is even possible with eyra to avoid entirely libc and using fully static rust binary.
|
||||
If you want to use the similar videos tool, you need to install ffmpeg (runtime dependency).
|
||||
If you want to use heif/libraw/libavif (build/runtime dependency), you need to install the required packages.
|
||||
|
||||
If you want to use similar videos tool, you need to install ffmpeg(runtime dependency).
|
||||
If you want to use heif/libraw/libavif(build/runtime dependency) you need to install required packages(may require
|
||||
bigger os version than czkawka).
|
||||
|
||||
- mac - `brew install ffmpeg libraw libheif libavif` - https://formulae.brew.sh/formula/ffmpeg
|
||||
- linux - `sudo apt install ffmpeg libraw-dev libheif-dev libavif-dev libdav1d-dev`
|
||||
- windows - `choco install ffmpeg` - or if not working, download from https://ffmpeg.org/download.html#build-windows and
|
||||
unpack to location with `czkawka_cli.exe`, heif and libraw are not supported on windows
|
||||
- macOS: `brew install ffmpeg libraw libheif libavif dav1d` – [ffmpeg formula](https://formulae.brew.sh/formula/ffmpeg)
|
||||
- Linux: `sudo apt install ffmpeg libraw-dev libheif-dev libavif-dev libdav1d-dev`
|
||||
- Windows: `choco install ffmpeg` – or, if not working, download from [ffmpeg.org](https://ffmpeg.org/download.html#build-windows) and
|
||||
unpack to the location with `czkawka_cli.exe`. Heif and libraw are not supported on Windows.
|
||||
|
||||
## Compilation
|
||||
|
||||
For compilation, you need to have installed Rust via rustup - https://rustup.rs/ and compile it e.g. via
|
||||
To compile, you need to have Rust installed via [rustup](https://rustup.rs/). Then, build with:
|
||||
|
||||
```shell
|
||||
cargo run --release --bin czkawka_cli
|
||||
```
|
||||
|
||||
you can enable additional features via
|
||||
You can enable additional features with:
|
||||
|
||||
```shell
|
||||
cargo run --release --bin czkawka_cli --features "heif,libraw,libavif"
|
||||
```
|
||||
|
||||
on linux to build fully static binary with eyra you need to use (this is only for crazy people, so just use command
|
||||
above if you don't know what you are doing)
|
||||
## How to use
|
||||
|
||||
The application includes concise help for each tool, which you can display by running:
|
||||
```
|
||||
czkawka_cli --help
|
||||
```
|
||||
You can also get detailed information about the parameters of a specific tool by running, for example:
|
||||
```
|
||||
czkawka_cli dup --help
|
||||
```
|
||||
|
||||
|
||||
Example usage:
|
||||
```shell
|
||||
rustup default nightly-2025-01-01 # or any newer nightly that works fine with eyra
|
||||
cd czkawka_cli
|
||||
cargo add eyra --rename=std
|
||||
echo 'fn main() { println!("cargo:rustc-link-arg=-nostartfiles"); }' > build.rs
|
||||
cd ..
|
||||
cargo build --release --bin czkawka_cli
|
||||
czkawka dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hash -f results.txt -D aeo
|
||||
czkawka empty-folders -d /home/rafal/rr /home/gateway -f results.txt
|
||||
czkawka big -d /home/rafal/ /home/piszczal -e /home/rafal/Roman -n 25 -x VIDEO -f results.txt
|
||||
czkawka empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt
|
||||
czkawka temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D
|
||||
czkawka music -d /home/rafal -e /home/rafal/Pulpit -z "artist,year, ARTISTALBUM, ALBUM___tiTlE" -f results.txt
|
||||
czkawka symlinks -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt
|
||||
```
|
||||
|
||||
## LICENSE
|
||||
|
@@ -26,7 +26,7 @@ pub enum Commands {
|
||||
#[clap(
|
||||
name = "dup",
|
||||
about = "Finds duplicate files",
|
||||
after_help = "EXAMPLE:\n czkawka dup -d /home/rafal - -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hash -f results.txt -D aeo"
|
||||
after_help = "EXAMPLE:\n czkawka dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hash -f results.txt -D aeo"
|
||||
)]
|
||||
Duplicates(DuplicatesArgs),
|
||||
#[clap(
|
||||
@@ -73,7 +73,7 @@ pub enum Commands {
|
||||
after_help = "EXAMPLE:\n czkawka broken -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt"
|
||||
)]
|
||||
BrokenFiles(BrokenFilesArgs),
|
||||
#[clap(name = "video", about = "Finds similar video files", after_help = "EXAMPLE:\n czkawka videos -d /home/rafal -f results.txt")]
|
||||
#[clap(name = "video", about = "Finds similar video files", after_help = "EXAMPLE:\n czkawka video -d /home/rafal -f results.txt")]
|
||||
SimilarVideos(SimilarVideosArgs),
|
||||
#[clap(
|
||||
name = "ext",
|
||||
@@ -281,8 +281,8 @@ pub struct SameMusicArgs {
|
||||
long,
|
||||
default_value = "track_title,track_artist",
|
||||
value_parser = parse_music_duplicate_type,
|
||||
help = "Search method (track_title,track_artist,year,bitrate,genre,length))",
|
||||
long_help = "Sets which rows must be equal to set this files as duplicates(may be mixed, but must be divided by commas)."
|
||||
help = "Search method (track_title,track_artist,year,bitrate,genre,length)",
|
||||
long_help = "Sets which rows must be equal to set these files as duplicates (may be mixed, but must be divided by commas)."
|
||||
)]
|
||||
pub music_similarity: MusicSimilarity,
|
||||
#[clap(
|
||||
@@ -291,7 +291,7 @@ pub struct SameMusicArgs {
|
||||
default_value = "TAGS",
|
||||
value_parser = parse_checking_method_same_music,
|
||||
help = "Search method (CONTENT, TAGS)",
|
||||
long_help = "Methods to search files.\nCONTENT - finds similar audio files by content, TAGS - finds similar images by tags, needs to set"
|
||||
long_help = "Methods to search files.\nCONTENT - finds similar audio files by content, TAGS - finds similar music by tags."
|
||||
)]
|
||||
pub search_method: CheckingMethod,
|
||||
#[clap(
|
||||
@@ -724,7 +724,8 @@ fn parse_similar_images_similarity(src: &str) -> Result<SimilarityPreset, &'stat
|
||||
"medium" => Ok(SimilarityPreset::Medium),
|
||||
"high" => Ok(SimilarityPreset::High),
|
||||
"veryhigh" => Ok(SimilarityPreset::VeryHigh),
|
||||
_ => Err("Couldn't parse the image similarity preset (allowed: Minimal, VerySmall, Small, Medium, High, VeryHigh)"),
|
||||
"original" => Ok(SimilarityPreset::Original),
|
||||
_ => Err("Couldn't parse the image similarity preset (allowed: Minimal, VerySmall, Small, Medium, High, VeryHigh, Original)"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,7 +832,7 @@ OPTIONS:
|
||||
SUBCOMMANDS:
|
||||
{subcommands}
|
||||
|
||||
try "{usage} -h" to get more info about a specific tool
|
||||
try "{usage} <COMMAND> -h" to get more info about a specific tool
|
||||
|
||||
EXAMPLES:
|
||||
{bin} dup -d /home/rafal -e /home/rafal/Obrazy -m 25 -x 7z rar IMAGE -s hash -f results.txt -D aeo
|
||||
@@ -840,7 +841,7 @@ EXAMPLES:
|
||||
{bin} empty-files -d /home/rafal /home/szczekacz -e /home/rafal/Pulpit -R -f results.txt
|
||||
{bin} temp -d /home/rafal/ -E */.git */tmp* *Pulpit -f results.txt -D
|
||||
{bin} image -d /home/rafal -e /home/rafal/Pulpit -f results.txt
|
||||
{bin} music -d /home/rafal -e /home/rafal/Pulpit -z "artist,year, ARTISTALBUM, ALBUM___tiTlE" -f results.txt
|
||||
{bin} music -d /home/rafal -e /home/rafal/Pulpit -z \"artist,year,ARTISTALBUM,ALBUM___tiTlE\" -f results.txt
|
||||
{bin} symlinks -d /home/kicikici/ /home/szczek -e /home/kicikici/jestempsem -x jpg -f results.txt
|
||||
{bin} broken -d /home/mikrut/ -e /home/mikrut/trakt -f results.txt
|
||||
{bin} extnp -d /home/mikrut/ -e /home/mikrut/trakt -f results.txt"#;
|
||||
{bin} ext -d /home/mikrut/ -e /home/mikrut/trakt -f results.txt"#;
|
||||
|
@@ -67,7 +67,6 @@ pub(crate) fn get_progress_message(progress_data: &ProgressData) -> String {
|
||||
CurrentStage::SimilarVideosCalculatingHashes => "Reading similar values",
|
||||
CurrentStage::BrokenFilesChecking => "Checking broken files",
|
||||
CurrentStage::BadExtensionsChecking => "Checking extensions of files",
|
||||
|
||||
CurrentStage::DeletingFiles => "Deleting files/folders",
|
||||
_ => unreachable!("Unsupported stage {:?}", progress_data.sstage),
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ lofty = "0.22"
|
||||
# Needed by broken files
|
||||
zip = { version = "4.0", features = ["aes-crypto", "bzip2", "deflate", "time"], default-features = false }
|
||||
audio_checker = "0.1"
|
||||
lopdf = "0.36.0"
|
||||
lopdf = "0.37.0"
|
||||
|
||||
# Needed by audio similarity feature
|
||||
rusty-chromaprint = "0.3"
|
||||
|
@@ -131,29 +131,29 @@ pub(crate) fn get_raw_image(path: impl AsRef<Path> + std::fmt::Debug) -> Result<
|
||||
|
||||
let raw_source = RawSource::new(path.as_ref()).map_err(|err| format!("Failed to create RawSource from path {path:?}: {err}"))?;
|
||||
|
||||
timer.checkpoint("After creating RawSource");
|
||||
timer.checkpoint("Created RawSource");
|
||||
|
||||
let decoder = rawler::get_decoder(&raw_source).map_err(|e| e.to_string())?;
|
||||
|
||||
timer.checkpoint("After getting decoder");
|
||||
timer.checkpoint("Got decoder");
|
||||
let raw_image = decoder.raw_image(&raw_source, &RawDecodeParams::default(), false).map_err(|e| e.to_string())?;
|
||||
|
||||
timer.checkpoint("After decoding raw image");
|
||||
timer.checkpoint("Decoded raw image");
|
||||
|
||||
let developer = RawDevelop::default();
|
||||
let developed_image = developer.develop_intermediate(&raw_image).map_err(|e| e.to_string())?;
|
||||
|
||||
timer.checkpoint("After developing raw image");
|
||||
timer.checkpoint("Developed raw image");
|
||||
|
||||
let dynamic_image = developed_image.to_dynamic_image().ok_or("Failed to convert image to DynamicImage".to_string())?;
|
||||
|
||||
timer.checkpoint("After converting to DynamicImage");
|
||||
timer.checkpoint("Converted to DynamicImage");
|
||||
|
||||
let rgb_image = DynamicImage::from(dynamic_image.to_rgb8());
|
||||
|
||||
timer.checkpoint("After reconverting to RGB");
|
||||
timer.checkpoint("Reconverted to RGB");
|
||||
|
||||
trace!("{}", timer.report(false));
|
||||
trace!("{}", timer.report("Everything", false));
|
||||
|
||||
Ok(rgb_image)
|
||||
}
|
||||
|
@@ -1,13 +1,63 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// Timer for measuring elapsed time between checkpoints.
|
||||
///
|
||||
/// # How to use - examples
|
||||
///
|
||||
/// Basic usage:
|
||||
/// ```
|
||||
/// use czkawka_core::helpers::debug_timer::Timer;
|
||||
/// use std::thread::sleep;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let mut timer = Timer::new("MyTimer");
|
||||
/// sleep(Duration::from_millis(50));
|
||||
/// timer.checkpoint("step1");
|
||||
/// sleep(Duration::from_millis(30));
|
||||
/// timer.checkpoint("step2");
|
||||
/// let report = timer.report("all_steps", false);
|
||||
/// println!("{}", report);
|
||||
/// ```
|
||||
///
|
||||
/// Output example:
|
||||
/// ```text
|
||||
/// MyTimer - step1: 50.0ms,
|
||||
/// MyTimer - step2: 30.0ms,
|
||||
/// MyTimer - all_steps: 80.0ms
|
||||
/// ```
|
||||
///
|
||||
/// One-line output:
|
||||
/// ```
|
||||
/// use czkawka_core::helpers::debug_timer::Timer;
|
||||
/// use std::thread::sleep;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let mut timer = Timer::new("MyTimer");
|
||||
/// sleep(Duration::from_millis(10));
|
||||
/// timer.checkpoint("a");
|
||||
/// sleep(Duration::from_millis(20));
|
||||
/// timer.checkpoint("b");
|
||||
/// let report = timer.report("total", true);
|
||||
/// println!("{}", report);
|
||||
/// ```
|
||||
///
|
||||
/// Output example:
|
||||
/// ```text
|
||||
/// MyTimer - a: 10.0ms, b: 20.0ms, total: 30.0ms
|
||||
/// ```
|
||||
pub struct Timer {
|
||||
/// Name or label for the timer.
|
||||
base: String,
|
||||
/// Time when the timer was started.
|
||||
start_time: Instant,
|
||||
/// Time of the last checkpoint.
|
||||
last_time: Instant,
|
||||
/// List of (checkpoint name, duration since last checkpoint).
|
||||
times: Vec<(String, Duration)>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
/// Creates a new timer with a given label.
|
||||
pub fn new(base: &str) -> Self {
|
||||
Self {
|
||||
base: base.to_string(),
|
||||
@@ -17,21 +67,30 @@ impl Timer {
|
||||
}
|
||||
}
|
||||
|
||||
/// Records a checkpoint with the given name.
|
||||
pub fn checkpoint(&mut self, name: &str) {
|
||||
let elapsed = self.last_time.elapsed();
|
||||
self.times.push((name.to_string(), elapsed));
|
||||
self.last_time = Instant::now();
|
||||
}
|
||||
|
||||
pub fn report(&mut self, in_one_line: bool) -> String {
|
||||
/// Returns a formatted report of all checkpoints and total time.
|
||||
///
|
||||
/// If `in_one_line` is true, outputs all checkpoints in a single line.
|
||||
/// Otherwise, outputs each checkpoint on a separate line.
|
||||
pub fn report(&mut self, all_steps_name: &str, in_one_line: bool) -> String {
|
||||
let all_elapsed = self.start_time.elapsed();
|
||||
self.times.push(("Everything".to_string(), all_elapsed));
|
||||
self.times.push((all_steps_name.to_string(), all_elapsed));
|
||||
|
||||
let joiner = if in_one_line { ", " } else { ", \n" };
|
||||
self.times
|
||||
.iter()
|
||||
.map(|(name, time)| format!("{} - {name}: {time:?}", self.base))
|
||||
.collect::<Vec<_>>()
|
||||
.join(joiner)
|
||||
if in_one_line {
|
||||
let times = self.times.iter().map(|(name, time)| format!("{name}: {time:?}")).collect::<Vec<_>>().join(", ");
|
||||
format!("{} - {}", self.base, times)
|
||||
} else {
|
||||
self.times
|
||||
.iter()
|
||||
.map(|(name, time)| format!("{} - {name}: {time:?}", self.base))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", \n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,26 @@
|
||||
//! DelayedSender: A utility for batching or throttling messages sent between threads.
|
||||
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
/// A sender that delays sending values until a specified wait time has passed since the last sent value.
|
||||
///
|
||||
/// This is useful for batching updates or reducing the frequency of sending messages in a multi-threaded environment.
|
||||
/// It is not ideal - using mutexes in send function from multiple threads can lead to performance issues(waiting for mutex release), but at least for now I don't see too much performance impact.
|
||||
/// In future here could be used something like one element channel which would drop all other messages, but not sure if currently something like this exists
|
||||
/// Note: Using mutexes in the send function from multiple threads can lead to performance issues (waiting for mutex release),
|
||||
/// but for now, the performance impact is minimal. In the future, a more efficient channel could be used.
|
||||
pub struct DelayedSender<T: Send + 'static> {
|
||||
slot: Arc<Mutex<Option<T>>>,
|
||||
stop_flag: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl<T: Send + 'static> DelayedSender<T> {
|
||||
/// Creates a new DelayedSender.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `sender` - The channel sender to forward values to.
|
||||
/// * `wait_time` - The minimum duration to wait between sends.
|
||||
pub fn new(sender: crossbeam_channel::Sender<T>, wait_time: Duration) -> Self {
|
||||
let slot = Arc::new(Mutex::new(None));
|
||||
let slot_clone = Arc::clone(&slot);
|
||||
@@ -51,6 +59,7 @@ impl<T: Send + 'static> DelayedSender<T> {
|
||||
Self { slot, stop_flag }
|
||||
}
|
||||
|
||||
/// Sends a value, replacing any previous value that has not yet been sent.
|
||||
pub fn send(&self, value: T) {
|
||||
let mut slot = self.slot.lock().expect("Failed to lock slot in DelayedSender");
|
||||
*slot = Some(value);
|
||||
@@ -59,8 +68,8 @@ impl<T: Send + 'static> DelayedSender<T> {
|
||||
|
||||
impl<T: Send + 'static> Drop for DelayedSender<T> {
|
||||
fn drop(&mut self) {
|
||||
// We need to know, that after dropping DelayedSender, no more values will be sent
|
||||
// Previously some values were cached and sent after other later operations
|
||||
// After dropping DelayedSender, no more values will be sent.
|
||||
// Previously, some values were cached and sent after later operations.
|
||||
self.stop_flag.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
@@ -1,29 +1,44 @@
|
||||
//! Messages: Utility for collecting and printing messages, warnings, and errors.
|
||||
|
||||
/// Stores messages, warnings, and errors for reporting.
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct Messages {
|
||||
/// Informational messages.
|
||||
pub messages: Vec<String>,
|
||||
/// Warning messages.
|
||||
pub warnings: Vec<String>,
|
||||
/// Error messages.
|
||||
pub errors: Vec<String>,
|
||||
}
|
||||
|
||||
impl Messages {
|
||||
/// Creates a new, empty `Messages` struct.
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
/// Creates a new `Messages` struct with errors.
|
||||
pub fn new_from_errors(errors: Vec<String>) -> Self {
|
||||
Self { errors, ..Default::default() }
|
||||
}
|
||||
|
||||
/// Creates a new `Messages` struct with warnings.
|
||||
pub fn new_from_warnings(warnings: Vec<String>) -> Self {
|
||||
Self { warnings, ..Default::default() }
|
||||
}
|
||||
|
||||
/// Creates a new `Messages` struct with messages.
|
||||
pub fn new_from_messages(messages: Vec<String>) -> Self {
|
||||
Self { messages, ..Default::default() }
|
||||
}
|
||||
|
||||
/// Prints all messages, warnings, and errors to the provided writer.
|
||||
pub fn print_messages_to_writer<T: std::io::Write>(&self, writer: &mut T) -> std::io::Result<()> {
|
||||
let text = self.create_messages_text();
|
||||
writer.write_all(text.as_bytes())
|
||||
}
|
||||
|
||||
/// Creates a formatted string containing all messages, warnings, and errors.
|
||||
pub fn create_messages_text(&self) -> String {
|
||||
let mut text_to_return: String = String::new();
|
||||
|
||||
@@ -38,7 +53,6 @@ impl Messages {
|
||||
|
||||
if !self.warnings.is_empty() {
|
||||
text_to_return += "-------------------------------WARNINGS--------------------------------\n";
|
||||
|
||||
for i in &self.warnings {
|
||||
text_to_return += i;
|
||||
text_to_return += "\n";
|
||||
@@ -48,7 +62,6 @@ impl Messages {
|
||||
|
||||
if !self.errors.is_empty() {
|
||||
text_to_return += "--------------------------------ERRORS---------------------------------\n";
|
||||
|
||||
for i in &self.errors {
|
||||
text_to_return += i;
|
||||
text_to_return += "\n";
|
||||
@@ -59,6 +72,7 @@ impl Messages {
|
||||
text_to_return
|
||||
}
|
||||
|
||||
/// Extends this `Messages` struct with another, appending all messages, warnings, and errors.
|
||||
pub fn extend_with_another_messages(&mut self, messages: Self) {
|
||||
let (messages, warnings, errors) = (messages.messages, messages.warnings, messages.errors);
|
||||
self.messages.extend(messages);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
/// Helpers, generic modules, traits, structs, ready to copy/paste to other projects
|
||||
//! Helper modules: generic utilities, traits, structs, ready to copy/paste to other projects.
|
||||
|
||||
pub mod debug_timer;
|
||||
pub mod delayed_sender;
|
||||
pub mod messages;
|
||||
|
@@ -134,11 +134,12 @@ impl SimilarVideos {
|
||||
return None;
|
||||
}
|
||||
|
||||
let size = file_entry.size;
|
||||
// Currently size is not too much relevant
|
||||
// let size = file_entry.size;
|
||||
let res = self.check_video_file_entry(file_entry);
|
||||
|
||||
progress_handler.increase_items(1);
|
||||
progress_handler.increase_size(size);
|
||||
// progress_handler.increase_size(size);
|
||||
|
||||
Some(res)
|
||||
})
|
||||
|
@@ -1,79 +1,91 @@
|
||||
# Czkawka GUI
|
||||
|
||||
Czkawka GUI is a graphical user interface for Czkawka Core written with GTK 4.
|
||||
Czkawka GUI is a graphical user interface for Czkawka Core, built with GTK 4.
|
||||
|
||||

|
||||
|
||||
## Maintenance mode
|
||||
## Maintenance Mode
|
||||
|
||||
Czkawka Gtk is currently in maintenance mode.
|
||||
While no new features will be added (at least by me), bug fixes and compatibility updates with the Czkawka core package will continue to be provided.
|
||||
Czkawka GTK is currently in maintenance mode.
|
||||
No new features will be added (at least by me), but bug fixes and compatibility updates with the Czkawka core package will continue.
|
||||
Active development is now focused on the Krokiet GUI.
|
||||
|
||||
## Requirements
|
||||
|
||||
Requirements depend on your platform.
|
||||
|
||||
Prebuilt binaries are available here - https://github.com/qarmin/czkawka/releases/
|
||||
Prebuilt binaries are available here: https://github.com/qarmin/czkawka/releases/
|
||||
|
||||
Additional features like heif, libraw, libavif require additional libraries to be installed, and may increase
|
||||
Additional features such as HEIF, libraw, and libavif require extra libraries to be installed, which may increase the number of dependencies.
|
||||
|
||||
### Linux
|
||||
|
||||
#### Prebuild binaries
|
||||
#### Prebuilt binaries / Self-compiled
|
||||
|
||||
Ubuntu - `sudo apt install libgtk-4-bin libheif1 libraw-bin ffmpeg -y`
|
||||
Ubuntu:
|
||||
`sudo apt install libgtk-4-bin libheif1 libraw-bin ffmpeg -y`
|
||||
|
||||
### Mac
|
||||
|
||||
```
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
brew install gtk4 ffmpeg librsvg libheif libraw dav1d
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
#### Prebuilt binaries
|
||||
All required libraries are bundled in the zip (except ffmpeg, which you can install manually and place `ffmpeg.exe` in a directory included in your system PATH).
|
||||
|
||||
## Installation
|
||||
|
||||
### Prebuilt binaries (All OS)
|
||||
After installing the required dependencies, download the prebuilt binaries for your platform from the [releases page](https://github.com/qarmin/czkawka/releases).
|
||||
|
||||
### Linux
|
||||
|
||||
#### Flatpak
|
||||
```
|
||||
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
flatpak install flathub com.github.qarmin.czkawka
|
||||
```
|
||||
|
||||
none - all needed libraries are bundled - https://flathub.org/apps/com.github.qarmin.czkawka
|
||||
#### Debian package (Unofficial)
|
||||
Requires Debian 13 (or derivatives) or later.
|
||||
```
|
||||
sudo apt install czkawka_gui
|
||||
```
|
||||
|
||||
#### PPA(Unofficial)
|
||||
#### PPA (Unofficial) - Debian-based distributions (Ubuntu, Linux Mint, etc.)
|
||||
```
|
||||
sudo add-apt-repository ppa:xtradeb/apps
|
||||
sudo apt update
|
||||
sudo apt install czkawka
|
||||
```
|
||||
[PPA page](https://launchpad.net/~xtradeb/+archive/ubuntu/apps)
|
||||
|
||||
### Mac
|
||||
|
||||
### Homebrew
|
||||
|
||||
Czkawka gui is available in homebrew - https://formulae.brew.sh/formula/czkawka and can be installed via
|
||||
|
||||
#### Homebrew (Unofficial)
|
||||
```
|
||||
brew install czkawka
|
||||
```
|
||||
|
||||
### Manual installation requirements
|
||||
|
||||
```
|
||||
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
brew install gtk4 adwaita-icon-theme ffmpeg librsvg libheif libraw
|
||||
|
||||
```
|
||||
[Formula page](https://formulae.brew.sh/formula/czkawka)
|
||||
|
||||
### Windows
|
||||
|
||||
All needed libraries should be bundled in zip (except ffmpeg which you need download and unpack to location
|
||||
with `czkawka_gui.exe` - https://ffmpeg.org/download.html#build-windows)
|
||||
|
||||
You can also install the app via msys2 (webp and heif should work here) - https://www.msys2.org/#installation (czkawka
|
||||
package - https://packages.msys2.org/base/mingw-w64-czkawka)
|
||||
|
||||
#### MSYS2 (Unofficial)
|
||||
```
|
||||
|
||||
pacman -S mingw-w64-x86_64-czkawka-gui
|
||||
|
||||
```
|
||||
[Package link](https://packages.msys2.org/base/mingw-w64-czkawka)
|
||||
|
||||
and you can create a shortcut to `C:\msys64\mingw64\bin\czkawka_gui.exe`
|
||||
The file should be installed to `C:\msys64\mingw64\bin\czkawka_gui.exe` and can be run from there.
|
||||
This version is likely the most feature-complete on Windows, as it is compiled with optional features enabled.
|
||||
|
||||
## Compilation
|
||||
|
||||
Compiling the gui is harder than compiling cli or core, because it uses gtk4 which is written in C and also requires a
|
||||
lot build and runtime dependencies.
|
||||
Compiling the GUI is more complex than compiling the CLI, core, or Krokiet, because it uses GTK4 (written in C) and requires many build and runtime dependencies.
|
||||
|
||||
### Requirements
|
||||
|
||||
@@ -84,7 +96,7 @@ lot build and runtime dependencies.
|
||||
|
||||
The Rust version corresponds to the latest rustc available in Debian Sid: https://packages.debian.org/sid/rustc
|
||||
|
||||
### Linux (Ubuntu, but on other OS should work similar)
|
||||
### Linux (Ubuntu; similar steps apply to other distributions)
|
||||
|
||||
```shell
|
||||
sudo apt install libgtk-4-dev -y # Base
|
||||
@@ -107,31 +119,26 @@ cargo run --release --bin czkawka_gui --features "heif,libraw,libavif"
|
||||
|
||||
### Windows
|
||||
|
||||
Currently, there is are no instructions on how to compile the app on Windows natively.</br>
|
||||
You can check for CI for instructions how to cross-compile the app from linux to windows (uses prebuilt docker
|
||||
image) - [CI Instructions](../.github/workflows/windows.yml)</br>
|
||||
There exists a mingw recipe which you can try to convert for your
|
||||
purposes - https://github.com/msys2/MINGW-packages/blob/master/mingw-w64-czkawka/PKGBUILD
|
||||
Currently, there are no instructions for compiling the app natively on Windows.</br>
|
||||
You can check the CI for instructions on how to cross-compile the app from Linux to Windows (using a prebuilt Docker image): [CI Instructions](../.github/workflows/windows.yml)</br>
|
||||
There is also a mingw recipe you can try to adapt for your needs: https://github.com/msys2/MINGW-packages/blob/master/mingw-w64-czkawka/PKGBUILD
|
||||
|
||||
## Limitations
|
||||
|
||||
Not all available features and/or components implemented here, this is the list of limitations:
|
||||
Not all features and components are implemented here. The main limitations are:
|
||||
|
||||
- Snap versions does not allow to use the similar videos feature
|
||||
- Windows version does not support heif and webp files with prebuilt binaries(msys2 version support them)
|
||||
- Prebuilt binaries for mac arm do not exist
|
||||
- On Windows, text may appear very small on high resolution displays, a solution is to manually change DPI scaling for
|
||||
this app, see:
|
||||
- [recommended fix](https://github.com/qarmin/czkawka/issues/787#issuecomment-1292253437) (modify gtk.css),
|
||||
- [or this workaround](https://github.com/qarmin/czkawka/issues/863#issuecomment-1416761308) (modify windows DPI settings for this app (this works too but the text is a bit blurry)).
|
||||
- The Windows version does not support HEIF and WebP files with prebuilt binaries (the MSYS2 version supports them).
|
||||
- On Windows, text may appear very small on high-resolution displays. You can manually change DPI scaling for this app:
|
||||
- [Recommended fix](https://github.com/qarmin/czkawka/issues/787#issuecomment-1292253437) (modify gtk.css)
|
||||
- [Alternative workaround](https://github.com/qarmin/czkawka/issues/863#issuecomment-1416761308) (modify Windows DPI settings for this app; this works too, but the text may be a bit blurry).
|
||||
|
||||
## License
|
||||
|
||||
Code is distributed under MIT license.
|
||||
The code is distributed under the MIT license.
|
||||
|
||||
Icon was created by [jannuary](https://github.com/jannuary) and licensed CC-BY-4.0.
|
||||
The icon was created by [jannuary](https://github.com/jannuary) and is licensed under CC-BY-4.0.
|
||||
|
||||
Windows dark theme is used from project [WhiteSur](https://github.com/slypy/whitesur-gtk4-theme) with MIT license.
|
||||
The Windows dark theme is from the [WhiteSur](https://github.com/slypy/whitesur-gtk4-theme) project, licensed under MIT.
|
||||
|
||||
The program is completely free to use.
|
||||
|
||||
@@ -139,15 +146,10 @@ The program is completely free to use.
|
||||
|
||||
## Name
|
||||
|
||||
Czkawka is a Polish word which means _hiccup_.
|
||||
Czkawka is a Polish word meaning _hiccup_.
|
||||
|
||||
I chose this name because I wanted to hear people speaking other languages pronounce it, so feel free to spell it the
|
||||
way you want.
|
||||
I chose this name because I wanted to hear people speaking other languages pronounce it, so feel free to say it however you like.
|
||||
|
||||
This name is not as bad as it seems, because I was also thinking about using words like _żółć_, _gżegżółka_ or _żołądź_,
|
||||
but I gave up on these ideas because they contained Polish characters, which would cause difficulty in searching for the
|
||||
project.
|
||||
This name is not as difficult as it seems; I also considered words like _żółć_, _gżegżółka_, or _żołądź_, but decided against them because they contain Polish characters, which would make searching for the project harder.
|
||||
|
||||
At the beginning of the program creation, if the response concerning the name was unanimously negative, I prepared
|
||||
myself
|
||||
for a possible change of the name of the program, and the opinions were extremely mixed.
|
||||
At the beginning of the project, if the response to the name was unanimously negative, I was prepared to change it, but the opinions were extremely mixed.
|
||||
|
@@ -43,9 +43,9 @@ pub(crate) fn connect_button_move(gui_data: &GuiData) {
|
||||
let mut folders: Vec<PathBuf> = Vec::new();
|
||||
let g_files = file_chooser.files();
|
||||
for index in 0..g_files.n_items() {
|
||||
let file = &g_files.item(index);
|
||||
let file = g_files.item(index);
|
||||
if let Some(file) = file {
|
||||
let ss = file.clone().downcast::<gtk4::gio::File>().expect("Failed to downcast to gio::File");
|
||||
let ss = file.downcast::<gtk4::gio::File>().expect("Failed to downcast to gio::File");
|
||||
if let Some(path_buf) = ss.path() {
|
||||
folders.push(path_buf);
|
||||
}
|
||||
@@ -190,7 +190,7 @@ fn move_files_common(
|
||||
let path = model.get::<String>(&iter, column_path);
|
||||
|
||||
let thing = get_full_name_from_path_name(&path, &file_name);
|
||||
let destination_file = destination_folder.join(file_name);
|
||||
let destination_file = destination_folder.join(&file_name);
|
||||
if Path::new(&thing).is_dir() {
|
||||
if let Err(e) = fs_extra::dir::move_dir(&thing, &destination_file, &CopyOptions::new()) {
|
||||
messages += flg!("move_folder_failed", name = thing, reason = e.to_string()).as_str();
|
||||
|
@@ -29,7 +29,7 @@ fn change_language(gui_data: &GuiData) {
|
||||
let lang_identifier = vec![LanguageIdentifier::from_bytes(lang_short.as_bytes()).expect("Failed to create LanguageIdentifier")];
|
||||
for (lib, localizer) in localizers {
|
||||
if let Err(error) = localizer.select(&lang_identifier) {
|
||||
error!("Error while loadings languages for {lib} {error:?}");
|
||||
error!("Error while loading languages for {lib} {error:?}");
|
||||
}
|
||||
}
|
||||
gui_data.update_language();
|
||||
@@ -41,7 +41,7 @@ pub(crate) fn load_system_language(gui_data: &GuiData) {
|
||||
if let Some(language) = requested_languages.first() {
|
||||
let old_short_lang = language.to_string();
|
||||
let mut short_lang = String::new();
|
||||
// removes from e.g. en_zb, ending _zd since Czkawka don't support this(maybe could add this in future, but only when)
|
||||
// removes from e.g. en_zb, ending _zd since Czkawka doesn't support this (maybe could add this in future)
|
||||
for i in old_short_lang.chars() {
|
||||
if i.is_ascii_alphabetic() {
|
||||
short_lang.push(i);
|
||||
@@ -49,18 +49,11 @@ pub(crate) fn load_system_language(gui_data: &GuiData) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// let mut found: bool = false;
|
||||
for (index, lang) in LANGUAGES_ALL.iter().enumerate() {
|
||||
if lang.short_text == short_lang {
|
||||
// found = true;
|
||||
gui_data.settings.combo_box_settings_language.set_active(Some(index as u32));
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if found {
|
||||
// println!("INFO: Default system language {} is available, so choosing them", short_lang);
|
||||
// } else {
|
||||
// println!("INFO: Default system language {} is not available, using English(en) instead", short_lang);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@@ -33,7 +33,6 @@ impl GuiAbout {
|
||||
"Rafał Mikrut",
|
||||
"Alexis Lefebvre",
|
||||
"Thomas Andreas Jung",
|
||||
"Alexis Lefebvre",
|
||||
"Peter Blackson",
|
||||
"TheEvilSkeleton",
|
||||
"Ben Bodenmiller",
|
||||
@@ -50,6 +49,7 @@ impl GuiAbout {
|
||||
"Aarni Koskela",
|
||||
"Adam Boguszewski",
|
||||
"Alex",
|
||||
"Andreas Gerstmayr",
|
||||
"AshesOfEther",
|
||||
"Caduser2020",
|
||||
"CalunVier",
|
||||
@@ -93,9 +93,11 @@ impl GuiAbout {
|
||||
"codingnewcode",
|
||||
"cyqsimon",
|
||||
"endolith",
|
||||
"freeducks-debug",
|
||||
"jann",
|
||||
"kamilek96",
|
||||
"kuskov",
|
||||
"leapwill",
|
||||
"rugk",
|
||||
"santiago fn",
|
||||
"tecome",
|
||||
|
@@ -348,7 +348,7 @@ pub(crate) fn get_list_store(tree_view: &TreeView) -> ListStore {
|
||||
}
|
||||
|
||||
pub(crate) fn get_dialog_box_child(dialog: >k4::Dialog) -> gtk4::Box {
|
||||
dialog.child().expect("Dialog have no chile").downcast::<gtk4::Box>().expect("Dialog child is not Box")
|
||||
dialog.child().expect("Dialog has no child").downcast::<gtk4::Box>().expect("Dialog child is not Box")
|
||||
}
|
||||
|
||||
pub(crate) fn change_dimension_to_krotka(dimensions: &str) -> (u64, u64) {
|
||||
|
@@ -30,7 +30,7 @@ pub const LANGUAGES_ALL: &[Language] = &[
|
||||
short_text: "uk",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "한국인 (Korean)",
|
||||
combo_box_text: "한국어 (Korean)",
|
||||
short_text: "ko",
|
||||
},
|
||||
Language {
|
||||
@@ -42,7 +42,7 @@ pub const LANGUAGES_ALL: &[Language] = &[
|
||||
short_text: "de",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "やまと (Japanese)",
|
||||
combo_box_text: "日本語 (Japanese)",
|
||||
short_text: "ja",
|
||||
},
|
||||
Language {
|
||||
@@ -70,27 +70,27 @@ pub const LANGUAGES_ALL: &[Language] = &[
|
||||
short_text: "no",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "Swedish (Svenska)",
|
||||
combo_box_text: "Svenska (Swedish)",
|
||||
short_text: "sv-SE",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "المملكة العربية السعودية (Saudi Arabia)",
|
||||
combo_box_text: "العربية (Arabic)",
|
||||
short_text: "ar",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "България (Bulgaria)",
|
||||
combo_box_text: "Български (Bulgarian)",
|
||||
short_text: "bg",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "Ελλάδα (Greece)",
|
||||
combo_box_text: "Ελληνικά (Greek)",
|
||||
short_text: "el",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "Nederland (Netherlands)",
|
||||
combo_box_text: "Nederlands (Dutch)",
|
||||
short_text: "nl",
|
||||
},
|
||||
Language {
|
||||
combo_box_text: "România (Romania)",
|
||||
combo_box_text: "Română (Romanian)",
|
||||
short_text: "ro",
|
||||
},
|
||||
];
|
||||
|
9
justfile
9
justfile
@@ -124,8 +124,11 @@ bloat:
|
||||
cargo bloat --release --crates --bin czkawka_gui
|
||||
cargo bloat --release --crates --bin krokiet
|
||||
|
||||
check_complilations:
|
||||
check_compilations:
|
||||
git checkout Cargo.toml
|
||||
# cargo install --path misc/test_compilation_speed_size
|
||||
test_compilation_speed_size misc/test_compilation_speed_size/krokiet.json
|
||||
python3 misc/test_compilation_speed_size/generate_md_and_plots.py
|
||||
test_compilation_speed_size misc/test_compilation_speed_size/test.json
|
||||
python3 misc/test_compilation_speed_size/generate_md_and_plots.py
|
||||
|
||||
tags:
|
||||
tags=($(git tag --sort=version:refname | grep -v Nightly)); for ((i=0; i<${#tags[@]}-1; i++)); do from=${tags[$i]}; to=${tags[$i+1]}; echo "$from -> $to : $(git diff --shortstat "$from" "$to")"; done; last=${tags[-1]}; echo "$last -> master : $(git diff --shortstat "$last" master)"
|
@@ -27,6 +27,7 @@ rayon = "1.10"
|
||||
fs_extra = "1.3" # TODO replace with less buggy library
|
||||
trash = "5.1"
|
||||
dunce = "1.0.5"
|
||||
fast_image_resize = { version = "=5.1.4", features = ["image"] } # TODO, greater versions uses unstable features, that were stabilized after 1.85.0
|
||||
|
||||
# Just for enums
|
||||
vid_dup_finder_lib = "0.4"
|
||||
|
@@ -75,6 +75,8 @@ pub enum StrDataEmptyFiles {
|
||||
pub enum IntDataTemporaryFiles {
|
||||
ModificationDatePart1,
|
||||
ModificationDatePart2,
|
||||
SizePart1,
|
||||
SizePart2,
|
||||
}
|
||||
#[repr(u8)]
|
||||
pub enum StrDataTemporaryFiles {
|
||||
@@ -264,8 +266,9 @@ impl ActiveTab {
|
||||
Self::SimilarMusic => IntDataSimilarMusic::SizePart1 as usize,
|
||||
Self::BrokenFiles => IntDataBrokenFiles::SizePart1 as usize,
|
||||
Self::BadExtensions => IntDataBadExtensions::SizePart1 as usize,
|
||||
Self::TemporaryFiles => IntDataTemporaryFiles::SizePart1 as usize,
|
||||
Self::Settings | Self::About => return None,
|
||||
Self::EmptyFolders | Self::InvalidSymlinks | Self::TemporaryFiles => return None,
|
||||
Self::EmptyFolders | Self::InvalidSymlinks => return None,
|
||||
};
|
||||
Some(res)
|
||||
}
|
||||
|
@@ -514,7 +514,7 @@ fn rows_select_all_by_mode(selection: &mut SelectionData, model: &ModelRc<MainLi
|
||||
}
|
||||
|
||||
fn rows_select_all_one_by_one(model: &ModelRc<MainListModel>) {
|
||||
let items_to_update = model.iter().filter_map(|e| if !e.selected_row && !e.header_row { Some(e) } else { None }).count();
|
||||
let items_to_update = model.iter().filter(|e| !e.selected_row && !e.header_row).count();
|
||||
trace!("[FAST][ONE_BY_ONE] select all {}/{} items", items_to_update, model.row_count());
|
||||
for id in 0..model.row_count() {
|
||||
let mut model_data = model
|
||||
|
@@ -826,7 +826,8 @@ fn prepare_data_model_temporary_files(fe: &TemporaryFileEntry) -> (ModelRc<Share
|
||||
.into(),
|
||||
]);
|
||||
let modification_split = split_u64_into_i32s(fe.get_modified_date());
|
||||
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1]);
|
||||
let size_split = split_u64_into_i32s(fe.size);
|
||||
let data_model_int = VecModel::from_slice(&[modification_split.0, modification_split.1, size_split.0, size_split.1]);
|
||||
(data_model_str, data_model_int)
|
||||
}
|
||||
////////////////////////////////////////// Broken Files
|
||||
|
@@ -1,13 +1,13 @@
|
||||
use std::path::Path;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use czkawka_core::common::image::{check_if_can_display_image, get_dynamic_image_from_path};
|
||||
use czkawka_core::helpers::debug_timer::Timer;
|
||||
use fast_image_resize::{FilterType, ResizeAlg, ResizeOptions, Resizer};
|
||||
use image::DynamicImage;
|
||||
use log::{debug, error};
|
||||
use slint::ComponentHandle;
|
||||
|
||||
use crate::{ActiveTab, Callabler, GuiState, MainWindow, Settings};
|
||||
|
||||
pub type ImageBufferRgba = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
||||
|
||||
pub(crate) fn connect_show_preview(app: &MainWindow) {
|
||||
@@ -39,17 +39,41 @@ pub(crate) fn connect_show_preview(app: &MainWindow) {
|
||||
|
||||
let path = Path::new(image_path.as_str());
|
||||
|
||||
let res = load_image(path);
|
||||
if let Some((load_time, img)) = res {
|
||||
let start_timer_convert_time = Instant::now();
|
||||
let slint_image = convert_into_slint_image(&img);
|
||||
let convert_time = start_timer_convert_time.elapsed();
|
||||
// Looks that resizing image before sending it to GUI is faster than resizing it in Slint
|
||||
// Additionally it fixes issues with
|
||||
if let Some((mut timer, img)) = load_image(path) {
|
||||
let img_to_use = if img.width() > 1024 || img.height() > 1024 {
|
||||
let bigger_side = img.width().max(img.height());
|
||||
let scale_factor = bigger_side as f32 / 1024.0;
|
||||
let new_width = (img.width() as f32 / scale_factor) as u32;
|
||||
let new_height = (img.height() as f32 / scale_factor) as u32;
|
||||
|
||||
let mut dst_img = DynamicImage::new(new_width, new_height, img.color());
|
||||
timer.checkpoint("creating new image buffer");
|
||||
|
||||
let resize_options = ResizeOptions::new().resize_alg(ResizeAlg::Interpolation(FilterType::Lanczos3));
|
||||
match Resizer::new().resize(&img, &mut dst_img, Some(&resize_options)) {
|
||||
Ok(()) => {
|
||||
timer.checkpoint("resizing image with fast-image-resize");
|
||||
dst_img
|
||||
}
|
||||
Err(_) => {
|
||||
let r = img.resize(new_width, new_height, image::imageops::Lanczos3);
|
||||
timer.checkpoint("resizing image with image-rs");
|
||||
r
|
||||
}
|
||||
}
|
||||
} else {
|
||||
img
|
||||
};
|
||||
|
||||
let slint_image = convert_into_slint_image(&img_to_use);
|
||||
timer.checkpoint("converting image to Slint image");
|
||||
|
||||
let start_set_time = Instant::now();
|
||||
gui_state.set_preview_image(slint_image);
|
||||
let set_time = start_set_time.elapsed();
|
||||
timer.checkpoint("setting image in GUI");
|
||||
|
||||
debug!("Loading image took: {load_time:?}, converting image took: {convert_time:?}, setting image took: {set_time:?}");
|
||||
debug!("{}", timer.report("total", true));
|
||||
set_preview_visible(&gui_state, Some(image_path.as_str()));
|
||||
} else {
|
||||
set_preview_visible(&gui_state, None);
|
||||
@@ -73,12 +97,12 @@ fn convert_into_slint_image(img: &DynamicImage) -> slint::Image {
|
||||
slint::Image::from_rgba8(buffer)
|
||||
}
|
||||
|
||||
fn load_image(image_path: &Path) -> Option<(Duration, DynamicImage)> {
|
||||
fn load_image(image_path: &Path) -> Option<(Timer, DynamicImage)> {
|
||||
if !image_path.is_file() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let load_img_start_timer = Instant::now();
|
||||
let mut debug_timer = Timer::new("Loading and converting image in slint");
|
||||
|
||||
let img = match get_dynamic_image_from_path(&image_path.to_string_lossy()) {
|
||||
Ok(img) => img,
|
||||
@@ -87,5 +111,8 @@ fn load_image(image_path: &Path) -> Option<(Duration, DynamicImage)> {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some((load_img_start_timer.elapsed(), img))
|
||||
|
||||
debug_timer.checkpoint("loading image");
|
||||
|
||||
Some((debug_timer, img))
|
||||
}
|
||||
|
@@ -4,7 +4,8 @@ use slint::{ComponentHandle, Model, ModelRc, VecModel};
|
||||
|
||||
use crate::common::connect_i32_into_u64;
|
||||
use crate::connect_row_selection::recalculate_small_selection_if_needed;
|
||||
use crate::{ActiveTab, Callabler, GuiState, MainListModel, MainWindow, SortMode};
|
||||
use crate::connect_translation::translate_sort_mode;
|
||||
use crate::{ActiveTab, Callabler, GuiState, MainListModel, MainWindow, SortMode, SortModel};
|
||||
|
||||
pub(crate) fn connect_sort(app: &MainWindow) {
|
||||
let a = app.as_weak();
|
||||
@@ -28,6 +29,46 @@ pub(crate) fn connect_sort(app: &MainWindow) {
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn connect_showing_proper_sort_buttons(app: &MainWindow) {
|
||||
set_sort_buttons(app);
|
||||
let a = app.as_weak();
|
||||
app.global::<Callabler>().on_tab_changed(move || {
|
||||
let app = a.upgrade().expect("Failed to upgrade app :(");
|
||||
set_sort_buttons(&app);
|
||||
});
|
||||
}
|
||||
|
||||
fn set_sort_buttons(app: &MainWindow) {
|
||||
let active_tab = app.global::<GuiState>().get_active_tab();
|
||||
let mut base_buttons = vec![
|
||||
SortMode::Checked,
|
||||
SortMode::FullName,
|
||||
SortMode::ItemName,
|
||||
SortMode::ModificationDate,
|
||||
SortMode::ParentName,
|
||||
SortMode::Reverse,
|
||||
SortMode::Selection,
|
||||
];
|
||||
|
||||
let additional_buttons = match active_tab.get_int_size_opt_idx() {
|
||||
Some(_) => vec![SortMode::Size],
|
||||
None => vec![],
|
||||
};
|
||||
|
||||
base_buttons.extend(additional_buttons);
|
||||
base_buttons.reverse();
|
||||
|
||||
let new_sort_model = base_buttons
|
||||
.into_iter()
|
||||
.map(|e| SortModel {
|
||||
name: translate_sort_mode(e),
|
||||
data: e,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
app.global::<GuiState>().set_sort_results_list(ModelRc::new(VecModel::from(new_sort_model)));
|
||||
}
|
||||
|
||||
mod sorts {
|
||||
use super::*;
|
||||
|
||||
|
@@ -44,7 +44,7 @@ pub const LANGUAGE_LIST: &[Language] = &[
|
||||
left_panel_size: 185.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "한국인 (Korean)",
|
||||
long_name: "한국어 (Korean)",
|
||||
short_name: "ko",
|
||||
left_panel_size: 145.0,
|
||||
},
|
||||
@@ -59,7 +59,7 @@ pub const LANGUAGE_LIST: &[Language] = &[
|
||||
left_panel_size: 155.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "やまと (Japanese)",
|
||||
long_name: "日本語 (Japanese)",
|
||||
short_name: "ja",
|
||||
left_panel_size: 155.0,
|
||||
},
|
||||
@@ -94,32 +94,32 @@ pub const LANGUAGE_LIST: &[Language] = &[
|
||||
left_panel_size: 135.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "Swedish (Svenska)",
|
||||
long_name: "Svenska (Swedish)",
|
||||
short_name: "sv-SE",
|
||||
left_panel_size: 130.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "المملكة العربية السعودية (Saudi Arabia)",
|
||||
long_name: "العربية (Arabic)",
|
||||
short_name: "ar",
|
||||
left_panel_size: 135.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "България (Bulgaria)",
|
||||
long_name: "Български (Bulgarian)",
|
||||
short_name: "bg",
|
||||
left_panel_size: 165.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "Ελλάδα (Greece)",
|
||||
long_name: "Ελληνικά (Greek)",
|
||||
short_name: "el",
|
||||
left_panel_size: 160.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "Nederland (Netherlands)",
|
||||
long_name: "Nederlands (Dutch)",
|
||||
short_name: "nl",
|
||||
left_panel_size: 165.0,
|
||||
},
|
||||
Language {
|
||||
long_name: "România (Romania)",
|
||||
long_name: "Română (Romanian)",
|
||||
short_name: "ro",
|
||||
left_panel_size: 140.0,
|
||||
},
|
||||
@@ -166,7 +166,7 @@ pub(crate) fn change_language(app: &MainWindow) {
|
||||
let lang_identifier = vec![LanguageIdentifier::from_bytes(lang_items.short_name.as_bytes()).expect("Failed to create LanguageIdentifier")];
|
||||
for (lib, localizer) in localizers {
|
||||
if let Err(error) = localizer.select(&lang_identifier) {
|
||||
error!("Error while loadings languages for {lib} {error:?}");
|
||||
error!("Error while loading languages for {lib} {error:?}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,3 +417,16 @@ pub(crate) fn translate_select_mode(select_mode: SelectMode) -> SharedString {
|
||||
SelectMode::SelectOldest => flk!("selection_oldest").into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn translate_sort_mode(sort_mode: SortMode) -> SharedString {
|
||||
match sort_mode {
|
||||
SortMode::ItemName => flk!("sort_by_item_name").into(),
|
||||
SortMode::ParentName => flk!("sort_by_parent_name").into(),
|
||||
SortMode::FullName => flk!("sort_by_full_name").into(),
|
||||
SortMode::Size => flk!("sort_by_size").into(),
|
||||
SortMode::ModificationDate => flk!("sort_by_modification_date").into(),
|
||||
SortMode::Selection => flk!("sort_by_selection").into(),
|
||||
SortMode::Checked => flk!("sort_by_checked").into(),
|
||||
SortMode::Reverse => flk!("sort_reverse").into(),
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ use crate::connect_save::connect_save;
|
||||
use crate::connect_scan::connect_scan_button;
|
||||
use crate::connect_select::{connect_select, connect_showing_proper_select_buttons};
|
||||
use crate::connect_show_preview::connect_show_preview;
|
||||
use crate::connect_sort::connect_sort;
|
||||
use crate::connect_sort::{connect_showing_proper_sort_buttons, connect_sort};
|
||||
use crate::connect_stop::connect_stop_button;
|
||||
use crate::connect_translation::connect_translations;
|
||||
// TODO - at start this should be used, to be sure that rust models are in sync with slint models
|
||||
@@ -105,6 +105,7 @@ fn main() {
|
||||
connect_save(&app, Arc::clone(&shared_models));
|
||||
connect_row_selections(&app);
|
||||
connect_sort(&app);
|
||||
connect_showing_proper_sort_buttons(&app);
|
||||
|
||||
// Popups gather their size, after starting/closing popup at least once
|
||||
// This is simpler solution, than setting sizes of popups manually for each language
|
||||
|
@@ -4,11 +4,11 @@ use crate::MainListModel;
|
||||
|
||||
pub(crate) fn get_main_list_model() -> MainListModel {
|
||||
MainListModel {
|
||||
selected_row: false,
|
||||
val_int: Default::default(),
|
||||
checked: false,
|
||||
filled_header_row: false,
|
||||
header_row: false,
|
||||
selected_row: false,
|
||||
val_int: Default::default(),
|
||||
val_str: Default::default(),
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ export global ColorPalette {
|
||||
|
||||
in-out property <color> line_item_color: Settings.dark_theme ? #222222 : #dddddd;
|
||||
|
||||
in-out property <color> remove_item_color_button: Settings.dark_theme ? #222222 : #dddddd;
|
||||
|
||||
public pure function get_listview_color(selected: bool, hovered: bool) -> color {
|
||||
if (selected) {
|
||||
return hovered ? self.list_view_item_selected_hovered_color : self.list_view_item_selected_color;
|
||||
|
@@ -64,7 +64,7 @@ export component IncludedDirectories {
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: 7.5px;
|
||||
spacing: 5px;
|
||||
spacing: 0px;
|
||||
width: parent.width;
|
||||
|
||||
CheckBox {
|
||||
@@ -75,20 +75,26 @@ export component IncludedDirectories {
|
||||
width: size_referenced_folder;
|
||||
}
|
||||
|
||||
|
||||
HorizontalLayout {
|
||||
Text {
|
||||
horizontal-stretch: 1.0;
|
||||
height: parent.height;
|
||||
text: r.path;
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: left;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
width: parent.width;
|
||||
padding-left: 5px;
|
||||
Rectangle { }
|
||||
Rectangle {
|
||||
width: 50px;
|
||||
background: ColorPalette.remove_item_color_button;
|
||||
horizontal-stretch: 1.0;
|
||||
padding-left: 5px;
|
||||
Text {
|
||||
horizontal-stretch: 1.0;
|
||||
height: parent.height;
|
||||
text: r.path;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 50px;
|
||||
horizontal-stretch: 1.0;
|
||||
icon: @image-url("../icons/krokiet_delete.svg");
|
||||
colorize-icon: true;
|
||||
clicked => {
|
||||
@@ -153,23 +159,30 @@ export component ExcludedDirectories {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width;
|
||||
horizontal-stretch: 1.0;
|
||||
height: parent.height;
|
||||
text: r.path;
|
||||
vertical-alignment: center;
|
||||
horizontal-alignment: left;
|
||||
x: 5px;
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
width: parent.width;
|
||||
padding-left: 5px;
|
||||
Text {
|
||||
horizontal-stretch: 1.0;
|
||||
height: parent.height;
|
||||
text: r.path;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Button {
|
||||
Rectangle { }
|
||||
Rectangle {
|
||||
width: 50px;
|
||||
background: ColorPalette.remove_item_color_button;
|
||||
horizontal-stretch: 1.0;
|
||||
icon: @image-url("../icons/krokiet_delete.svg");
|
||||
colorize-icon: true;
|
||||
clicked => {
|
||||
Callabler.remove_item_directories(false, idx);
|
||||
Button {
|
||||
width: 50px;
|
||||
icon: @image-url("../icons/krokiet_delete.svg");
|
||||
colorize-icon: true;
|
||||
clicked => {
|
||||
Callabler.remove_item_directories(false, idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -8,9 +8,9 @@ export global Settings {
|
||||
in-out property <int> language_index: 0;
|
||||
in-out property <string> language_value: "English";
|
||||
|
||||
in-out property <[IncludedDirectoriesModel]> included_directories_model: [{ path: "/home/path", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false }];
|
||||
in-out property <[IncludedDirectoriesModel]> included_directories_model: [{ path: "/home/pathssssssssssssssssssssss", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false },{ path: "/home/path", referenced_folder: false, selected_row: false }];
|
||||
in-out property <int> included_directories_model_selected_idx: -1;
|
||||
in-out property <[ExcludedDirectoriesModel]> excluded_directories_model: [{ path:"/home/path", selected_row: false },{ path:"/home/path", selected_row: false },{ path:"/home/path", selected_row: false },{ path:"/home/path", selected_row: false }, { path:"/home/a", selected_row: false }];
|
||||
in-out property <[ExcludedDirectoriesModel]> excluded_directories_model: [{ path:"/home/pathssssssssssssssssssssssss", selected_row: false },{ path:"/home/path", selected_row: false },{ path:"/home/path", selected_row: false },{ path:"/home/path", selected_row: false }, { path:"/home/a", selected_row: false }];
|
||||
in-out property <int> excluded_directories_model_selected_idx: -1;
|
||||
|
||||
// Settings
|
||||
|
@@ -9,10 +9,7 @@ humansize = "2.1"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.142"
|
||||
strum = { version = "0.27.2", features = ["strum_macros"] }
|
||||
#polars = { version = "0.50.0", features = ["full"] }
|
||||
#polars-io ="0.50.0"
|
||||
#polars-core = "0.50.0"
|
||||
#plotters = "0.3.7"
|
||||
plotters = { version = "0.3.7", features = ["all_elements", "all_series"] }
|
||||
|
||||
[profile.release]
|
||||
panic = "unwind"
|
||||
|
12
misc/test_compilation_speed_size/README.md
Normal file
12
misc/test_compilation_speed_size/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
# Test compilation speed size
|
||||
This project is used to get the compilation speed and size of generated rust binaries for different configurations.
|
||||
|
||||
## How to use?
|
||||
- install app `cargo install --path .`
|
||||
- configure json config, just like `test.json` or `krokiet.json` - allowed values you can find in `src/model.rs`
|
||||
- go to your project root(if project is in workspace, you need to go to the workspace root)
|
||||
- run `test_compilation_speed_size config.json`
|
||||
- fix compilation errors if any happens(mixing some compilation flags can cause compilation errors)
|
||||
- install python dependencies with `sudo apt install python3-matplotlib python3-pandas python3-tabulate` or similar command
|
||||
- generate charts with `python3 generate_md_and_plots.py`
|
||||
- generated md and png files will be in `charts` folder
|
@@ -47,7 +47,7 @@ def plot_barh(
|
||||
data = data.dropna(subset=[value_col])
|
||||
data_sorted = data.sort_values(value_col, ascending=False)
|
||||
|
||||
plt.figure(figsize=(12, 10))
|
||||
plt.figure(figsize=(12, 10), dpi=300)
|
||||
bars = plt.barh(data_sorted[config_col], data_sorted[value_col] / unit_div, color=color)
|
||||
|
||||
ax = plt.gca()
|
||||
|
@@ -9,6 +9,10 @@
|
||||
"cranelift": "Both"
|
||||
},
|
||||
"build_config": [
|
||||
{
|
||||
"name": "debug",
|
||||
"rust_base_config": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "debug + debug disabled",
|
||||
"rust_base_config": "Debug",
|
||||
@@ -19,11 +23,20 @@
|
||||
"rust_base_config": "Debug",
|
||||
"build_or_check": "Check"
|
||||
},
|
||||
{
|
||||
"name": "release",
|
||||
"rust_base_config": "Release"
|
||||
},
|
||||
{
|
||||
"name": "release + debug info",
|
||||
"rust_base_config": "Release",
|
||||
"debug": "Full"
|
||||
},
|
||||
{
|
||||
"name": "release + native",
|
||||
"rust_base_config": "Release",
|
||||
"native": true
|
||||
},
|
||||
{
|
||||
"name": "release + opt o2",
|
||||
"rust_base_config": "Release",
|
||||
@@ -71,6 +84,14 @@
|
||||
"codegen_units": "One",
|
||||
"panic": "Abort"
|
||||
},
|
||||
{
|
||||
"name": "release + fat lto + cu 1 + panic abort + native",
|
||||
"rust_base_config": "Release",
|
||||
"lto": "Fat",
|
||||
"codegen_units": "One",
|
||||
"panic": "Abort",
|
||||
"native": true
|
||||
},
|
||||
{
|
||||
"name": "release + fat lto + cu 1 + panic abort + build-std",
|
||||
"rust_base_config": "Release",
|
||||
|
@@ -1,279 +0,0 @@
|
||||
// TODO - this is bigger task, simple changing almost 1:1 code from python to rust, not works
|
||||
|
||||
// use polars_core::prelude::*;
|
||||
// use polars_io::prelude::*;
|
||||
// use std::fs;
|
||||
// use plotters::prelude::*;
|
||||
//
|
||||
// fn load_dataframe_from_csv(file_path: &str) -> PolarsResult<DataFrame> {
|
||||
// let mut df = CsvReadOptions::default()
|
||||
// .with_has_header(true)
|
||||
// .map_parse_options(|opts| opts.with_separator(b'|'))
|
||||
// .try_into_reader_with_file_path(Some(file_path.into()))?
|
||||
// .finish()?;
|
||||
//
|
||||
// // Strip whitespace from string columns (bez unsafe)
|
||||
// let col_names: Vec<String> = df.get_column_names().iter().map(|s| s.to_string()).collect();
|
||||
// for name in col_names {
|
||||
// if let Ok(col) = df.column(&name) {
|
||||
// if matches!(col.dtype(), DataType::String) {
|
||||
// let trimmed = col.str()?.apply(|v| v.map(|s| std::borrow::Cow::Owned(s.trim().to_string())));
|
||||
// df.with_column(trimmed.into_series())?;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Convert columns to Float64, regardless of their current type
|
||||
// let try_parse_numeric = |df: &mut DataFrame, col_name: &str| -> PolarsResult<()> {
|
||||
// if let Ok(col) = df.column(col_name) {
|
||||
// let float_col = match col.dtype() {
|
||||
// DataType::Float64 => col.clone(),
|
||||
// DataType::Int64 => col.cast(&DataType::Float64)?,
|
||||
// DataType::Int32 => col.cast(&DataType::Float64)?,
|
||||
// DataType::UInt64 => col.cast(&DataType::Float64)?,
|
||||
// DataType::UInt32 => col.cast(&DataType::Float64)?,
|
||||
// DataType::String => {
|
||||
// let chunked = col
|
||||
// .str()?
|
||||
// .into_iter()
|
||||
// .map(|v| v.and_then(|s| s.parse::<f64>().ok()))
|
||||
// .collect::<Float64Chunked>();
|
||||
// chunked.into_series().into_column()
|
||||
// }
|
||||
// _ => col.cast(&DataType::Float64)?,
|
||||
// };
|
||||
// df.with_column(float_col)?;
|
||||
// }
|
||||
// Ok(())
|
||||
// };
|
||||
//
|
||||
// try_parse_numeric(&mut df, "Output File Size(in bytes)")?;
|
||||
// try_parse_numeric(&mut df, "Target Folder Size(in bytes)")?;
|
||||
// try_parse_numeric(&mut df, "Compilation Time(seconds)")?;
|
||||
// try_parse_numeric(&mut df, "Rebuild Time(seconds)")?;
|
||||
//
|
||||
// Ok(df)
|
||||
// }
|
||||
//
|
||||
// fn save_as_md(df: &DataFrame, path: &str) -> PolarsResult<()> {
|
||||
// // Save markdown table (without types, shape, or empty columns)
|
||||
// let columns: Vec<String> = df
|
||||
// .get_column_names()
|
||||
// .iter()
|
||||
// .filter(|c| !c.contains('(') && !c.contains("Thread"))
|
||||
// .map(|s| s.to_string())
|
||||
// .collect();
|
||||
// let mut subdf = df.select(&columns)?;
|
||||
//
|
||||
// // Remove last column if it's empty or all nulls
|
||||
// let drop_col = subdf
|
||||
// .get_columns()
|
||||
// .last()
|
||||
// .filter(|last| last.null_count() == last.len())
|
||||
// .map(|last| last.name().to_string());
|
||||
// if let Some(col_name) = drop_col {
|
||||
// subdf.drop_in_place(&col_name)?;
|
||||
// }
|
||||
//
|
||||
// // Write markdown manually (no types, no shape, no quotes for strings)
|
||||
// let mut md = String::new();
|
||||
// // Header
|
||||
// let header: Vec<String> = subdf.get_column_names().iter().map(|s| s.to_string()).collect();
|
||||
// md.push('|');
|
||||
// md.push_str(&header.join("|"));
|
||||
// md.push_str("|\n|");
|
||||
// md.push_str(&header.iter().map(|_| "---").collect::<Vec<_>>().join("|"));
|
||||
// md.push_str("|\n");
|
||||
// // Rows
|
||||
// for row in 0..subdf.height() {
|
||||
// md.push('|');
|
||||
// for (i, col) in subdf.get_columns().iter().enumerate() {
|
||||
// let val = col.get(row);
|
||||
// let s = match val {
|
||||
// Ok(AnyValue::Null) => "".to_string(),
|
||||
// Ok(v) => {
|
||||
// let mut s = v.to_string();
|
||||
// // Remove surrounding quotes if present
|
||||
// if s.starts_with('"') && s.ends_with('"') && s.len() > 1 {
|
||||
// s = s[1..s.len()-1].to_string();
|
||||
// }
|
||||
// s
|
||||
// },
|
||||
// Err(_) => "".to_string(),
|
||||
// };
|
||||
// md.push_str(&s);
|
||||
// if i != subdf.width() - 1 {
|
||||
// md.push('|');
|
||||
// }
|
||||
// }
|
||||
// md.push_str("|\n");
|
||||
// }
|
||||
// std::fs::write(path, md)?;
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// fn plot_barh(
|
||||
// df: &DataFrame,
|
||||
// value_col: &str,
|
||||
// xlabel: &str,
|
||||
// title: &str,
|
||||
// filename: &str,
|
||||
// _fmt: &str,
|
||||
// unit_div: f64,
|
||||
// dropna: bool,
|
||||
// color: RGBColor,
|
||||
// ) -> PolarsResult<()> {
|
||||
// // Helper to find a Series by name, handling possible quotes and whitespace
|
||||
// fn find_col<'a>(df: &'a DataFrame, name: &str) -> Option<&'a Series> {
|
||||
// let name_trimmed = name.trim();
|
||||
// println!("Columns {:?}", df.get_column_names());
|
||||
// println!("Looking for column: {}", name_trimmed);
|
||||
// df.get_columns().iter().find_map(|s| {
|
||||
// let sname = s.name().trim();
|
||||
// if sname == name_trimmed
|
||||
// || sname == name_trimmed.replace('\"', "")
|
||||
// || sname == format!("\"{}\"", name_trimmed)
|
||||
// {
|
||||
// Some(s)
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }).and_then(|e|e.as_series())
|
||||
// }
|
||||
//
|
||||
// let config_col = find_col(df, "BuildConfig")
|
||||
// .or_else(|| find_col(df, "Config"))
|
||||
// .ok_or_else(|| polars_core::error::PolarsError::ComputeError("Config column not found".into()))?;
|
||||
//
|
||||
// let value_col = find_col(df, value_col)
|
||||
// .ok_or_else(|| polars_core::error::PolarsError::ComputeError(format!("{} not found", value_col).into()))?;
|
||||
//
|
||||
// // Use .str()? on Series
|
||||
// let config_utf8 = config_col.str()?.into_iter();
|
||||
// let value_f64 = value_col.f64()?.into_iter();
|
||||
//
|
||||
// let mut configs = Vec::new();
|
||||
// let mut values = Vec::new();
|
||||
//
|
||||
// for (conf, val) in config_utf8.zip(value_f64) {
|
||||
// if dropna && val.is_none() {
|
||||
// continue;
|
||||
// }
|
||||
// configs.push(conf.unwrap_or("").to_string());
|
||||
// values.push(val.unwrap_or(0.0) / unit_div);
|
||||
// }
|
||||
//
|
||||
// // Sort descending by value
|
||||
// let mut zipped: Vec<_> = configs.into_iter().zip(values.into_iter()).collect();
|
||||
// zipped.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
|
||||
// let (configs, values): (Vec<_>, Vec<_>) = zipped.into_iter().unzip();
|
||||
//
|
||||
// // Plot with plotters
|
||||
// let root = BitMapBackend::new(filename, (1200, 900)).into_drawing_area();
|
||||
// root.fill(&WHITE).unwrap();
|
||||
//
|
||||
// let max_val = values.iter().cloned().fold(0.0/0.0, f64::max);
|
||||
// let x_max = if max_val.is_finite() && max_val > 0.0 { max_val * 1.15 } else { 1.0 };
|
||||
//
|
||||
// let mut chart = ChartBuilder::on(&root)
|
||||
// .caption(title, ("Noto Sans", 30).into_font())
|
||||
// .margin(40)
|
||||
// .x_label_area_size(60)
|
||||
// .y_label_area_size(300)
|
||||
// .build_cartesian_2d(0f64..x_max, 0..configs.len())
|
||||
// .unwrap();
|
||||
//
|
||||
// chart
|
||||
// .configure_mesh()
|
||||
// .x_desc(xlabel)
|
||||
// .y_desc("")
|
||||
// .y_labels(configs.len())
|
||||
// .y_label_formatter(&|idx| configs.get(*idx).cloned().unwrap_or_default())
|
||||
// .x_label_formatter(&|x| format!("{}", x))
|
||||
// .axis_desc_style(("Noto Sans", 20))
|
||||
// .label_style(("Noto Sans", 16))
|
||||
// .draw()
|
||||
// .unwrap();
|
||||
//
|
||||
// for (i, v) in values.iter().enumerate() {
|
||||
// chart
|
||||
// .draw_series(std::iter::once(Rectangle::new(
|
||||
// [(0.0, i), (*v, i + 1)],
|
||||
// color.filled(),
|
||||
// )))
|
||||
// .unwrap()
|
||||
// .label(configs[i].clone())
|
||||
// .legend(move |(x, y)| {
|
||||
// Rectangle::new([(x, y - 5), (x + 20, y + 5)], color.filled())
|
||||
// });
|
||||
//
|
||||
// // Draw value label
|
||||
// chart
|
||||
// .draw_series(std::iter::once(Text::new(
|
||||
// format!("{}", v),
|
||||
// (*v, i),
|
||||
// ("Noto Sans", 16).into_font().color(&BLACK),
|
||||
// )))
|
||||
// .unwrap();
|
||||
// }
|
||||
//
|
||||
// root.present().unwrap();
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// pub fn create_graphs() -> PolarsResult<()> {
|
||||
// let df = load_dataframe_from_csv("compilation_results.txt")?;
|
||||
//
|
||||
// // Create output directory
|
||||
// fs::create_dir_all("charts")?;
|
||||
//
|
||||
// plot_barh(
|
||||
// &df,
|
||||
// "Compilation Time(seconds)",
|
||||
// "Compilation Time (seconds)",
|
||||
// "Compilation Time by Config",
|
||||
// "charts/compilation_time.png",
|
||||
// "{:.1}s",
|
||||
// 1.0,
|
||||
// false,
|
||||
// RGBColor(31, 119, 180),
|
||||
// )?;
|
||||
// plot_barh(
|
||||
// &df,
|
||||
// "Rebuild Time(seconds)",
|
||||
// "Rebuild Time (seconds)",
|
||||
// "Rebuild Time by Config",
|
||||
// "charts/rebuild_time.png",
|
||||
// "{:.1}s",
|
||||
// 1.0,
|
||||
// false,
|
||||
// RGBColor(255, 127, 14),
|
||||
// )?;
|
||||
// plot_barh(
|
||||
// &df,
|
||||
// "Output File Size(in bytes)",
|
||||
// "Output File Size (MB)",
|
||||
// "Output File Size by Config",
|
||||
// "charts/output_file_size.png",
|
||||
// "{:.1} MB",
|
||||
// 1024.0 * 1024.0,
|
||||
// true,
|
||||
// RGBColor(44, 160, 44),
|
||||
// )?;
|
||||
// plot_barh(
|
||||
// &df,
|
||||
// "Target Folder Size(in bytes)",
|
||||
// "Target Folder Size (GB)",
|
||||
// "Target Folder Size by Config",
|
||||
// "charts/target_folder_size.png",
|
||||
// "{:.1} GB",
|
||||
// 1024.0 * 1024.0 * 1024.0,
|
||||
// false,
|
||||
// RGBColor(214, 39, 40),
|
||||
// )?;
|
||||
//
|
||||
// save_as_md(&df, "charts/compilation_results.md")?;
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
@@ -1,4 +1,5 @@
|
||||
mod model;
|
||||
mod new_chart;
|
||||
|
||||
use crate::model::{
|
||||
BuildConfig, BuildOrCheck, Config, Panic, Project, Results,
|
||||
@@ -9,11 +10,13 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use walkdir::WalkDir;
|
||||
// use crate::new_chart::create_chart;
|
||||
|
||||
const PROFILE_NAME: &str = "fff";
|
||||
const RESULTS_FILE_NAME: &str = "compilation_results.txt";
|
||||
|
||||
fn main() {
|
||||
// create_chart(); // TODO currently is broken a little
|
||||
let Some(first_arg) = std::env::args().nth(1) else {
|
||||
eprintln!("Please provide a path to the configuration json file as the first argument.");
|
||||
exit(1);
|
||||
@@ -104,9 +107,10 @@ fn run_cargo_build(mold: bool, cranelift: bool, build_config: &BuildConfig, proj
|
||||
}
|
||||
let mut rust_flags = None;
|
||||
if mold {
|
||||
let to_add = "-C link-arg=-fuse-ld=mold";
|
||||
rust_flags = match rust_flags {
|
||||
None => Some("-C link-arg=-fuse-ld=mold".to_string()),
|
||||
Some(flags) => Some(format!("{} -C link-arg=-fuse-ld=mold", flags)),
|
||||
None => Some(to_add.to_string()),
|
||||
Some(flags) => Some(format!("{flags} {to_add}")),
|
||||
};
|
||||
}
|
||||
if build_config.build_std {
|
||||
@@ -116,6 +120,13 @@ fn run_cargo_build(mold: bool, cranelift: bool, build_config: &BuildConfig, proj
|
||||
command.args(["-Z", "build-std=std"]);
|
||||
}
|
||||
}
|
||||
if build_config.native {
|
||||
let to_add = "-C target-cpu=native";
|
||||
rust_flags = match rust_flags {
|
||||
None => Some(to_add.to_string()),
|
||||
Some(flags) => Some(format!("{flags} {to_add}")),
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(rust_flags) = rust_flags {
|
||||
command.env("RUSTFLAGS", rust_flags);
|
||||
|
@@ -40,6 +40,7 @@ pub struct BuildConfigRead {
|
||||
pub overflow_checks: Option<OverflowChecks>,
|
||||
pub incremental: Option<Incremental>,
|
||||
pub build_std: Option<bool>,
|
||||
pub native: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
@@ -56,6 +57,7 @@ pub struct BuildConfig {
|
||||
pub overflow_checks: OverflowChecks,
|
||||
pub incremental: Incremental,
|
||||
pub build_std: bool,
|
||||
pub native: bool,
|
||||
}
|
||||
|
||||
impl From<BuildConfigRead> for BuildConfig {
|
||||
@@ -74,6 +76,7 @@ impl From<BuildConfigRead> for BuildConfig {
|
||||
overflow_checks: OverflowChecks::Off,
|
||||
incremental: Incremental::Off,
|
||||
build_std: false,
|
||||
native: false,
|
||||
},
|
||||
RustBaseConfig::Debug => BuildConfig {
|
||||
name: "debug".to_string(),
|
||||
@@ -88,6 +91,7 @@ impl From<BuildConfigRead> for BuildConfig {
|
||||
overflow_checks: OverflowChecks::On,
|
||||
incremental: Incremental::On,
|
||||
build_std: false,
|
||||
native: false,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,6 +108,7 @@ impl From<BuildConfigRead> for BuildConfig {
|
||||
overflow_checks: config.overflow_checks.unwrap_or(base_config.overflow_checks),
|
||||
incremental: config.incremental.unwrap_or(base_config.incremental),
|
||||
build_std: config.build_std.unwrap_or(false),
|
||||
native: config.native.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
87
misc/test_compilation_speed_size/src/new_chart.rs
Normal file
87
misc/test_compilation_speed_size/src/new_chart.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use plotters::prelude::*;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{BufRead, BufReader}; use plotters::style::text_anchor::Pos;
|
||||
use plotters::style::text_anchor::VPos; use plotters::style::text_anchor::HPos;
|
||||
|
||||
pub fn create_chart() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Prepare output directory
|
||||
fs::create_dir_all("charts")?;
|
||||
|
||||
// Open and read the file
|
||||
let file = File::open("compilation_results.txt")?;
|
||||
let reader = BufReader::new(file);
|
||||
|
||||
// Read header and find column indices
|
||||
let mut lines = reader.lines();
|
||||
let header = lines.next().unwrap()?;
|
||||
let headers: Vec<&str> = header.split('|').collect();
|
||||
let config_idx = headers.iter().position(|&h| h.trim() == "BuildConfig").unwrap();
|
||||
let time_idx = headers.iter().position(|&h| h.trim() == "Compilation Time(seconds)").unwrap();
|
||||
|
||||
// Parse data
|
||||
let mut data = Vec::new();
|
||||
for line in lines {
|
||||
let line = line?;
|
||||
let cols: Vec<&str> = line.split('|').collect();
|
||||
if cols.len() <= time_idx { continue; }
|
||||
let config = cols[config_idx].trim();
|
||||
let time: f64 = cols[time_idx].trim().parse().unwrap_or(0.0);
|
||||
data.push((config.to_string(), time));
|
||||
}
|
||||
|
||||
// Sort by time descending
|
||||
data.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
|
||||
|
||||
// Plot
|
||||
let root = BitMapBackend::new("charts/compilation_time.png", (1200, 800)).into_drawing_area();
|
||||
root.fill(&WHITE)?;
|
||||
|
||||
let max_time = data.iter().map(|(_, t)| *t).fold(0.0, f64::max);
|
||||
|
||||
let mut chart = ChartBuilder::on(&root)
|
||||
.caption("Compilation Time by Config", ("Noto Sans", 50))
|
||||
.margin(20)
|
||||
.x_label_area_size(80)
|
||||
.y_label_area_size(500)
|
||||
.build_cartesian_2d(0f64..(max_time * 1.2), 0..data.len())?;
|
||||
|
||||
chart
|
||||
.configure_mesh()
|
||||
.x_desc("Compilation Time (seconds)")
|
||||
.y_labels(data.len())
|
||||
.y_label_formatter(&|idx| {
|
||||
if let Some((label, _)) = data.get(*idx) {
|
||||
label.clone()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
})
|
||||
.y_label_style(("Noto Sans", 30).into_font().style(FontStyle::Bold))
|
||||
.x_label_style(("Noto Sans", 30).into_font())
|
||||
.y_label_offset(-150)
|
||||
.draw()?;
|
||||
|
||||
chart.draw_series(
|
||||
data.iter().enumerate().map(|(i, (_label, value))| {
|
||||
Rectangle::new(
|
||||
[(0.0, i), (*value, i + 1)],
|
||||
BLUE.filled(),
|
||||
)
|
||||
}),
|
||||
)?;
|
||||
|
||||
chart.draw_series(
|
||||
data.iter().enumerate().map(|(i, (_label, value))| {
|
||||
let x = *value;
|
||||
let y = i;
|
||||
Text::new(
|
||||
format!("{:.2}", value),
|
||||
(x, y),
|
||||
("Noto Sans", 35).into_font().color(&RED).pos(Pos::new(HPos::Right, VPos::Center)),
|
||||
)
|
||||
}),
|
||||
)?;
|
||||
|
||||
root.present()?;
|
||||
Ok(())
|
||||
}
|
Reference in New Issue
Block a user