From c06335fc3345831c973cd374c71012f7a489586b Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 13:08:52 +0100 Subject: [PATCH 01/10] implement egui-file-dialog for saving --- Cargo.lock | 496 ++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 2 +- src/gui.rs | 161 +++++++++++++---- src/main.rs | 13 +- 4 files changed, 440 insertions(+), 232 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c62796..5b7190a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -282,28 +282,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "ashpd" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" -dependencies = [ - "async-fs 2.1.2", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand", - "raw-window-handle", - "serde", - "serde_repr", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus 5.2.0", -] - [[package]] name = "async-broadcast" version = "0.5.1" @@ -433,17 +411,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io 2.4.0", - "blocking", - "futures-lite 2.5.0", -] - [[package]] name = "async-process" version = "1.8.1" @@ -696,6 +663,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytecount" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" + [[package]] name = "bytemuck" version = "1.21.0" @@ -760,6 +733,37 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", +] + [[package]] name = "cc" version = "1.2.5" @@ -1102,6 +1106,27 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1149,6 +1174,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ecolor" version = "0.30.0" @@ -1216,6 +1247,22 @@ dependencies = [ "serde", ] +[[package]] +name = "egui-file-dialog" +version = "0.8.0" +source = "git+https://github.com/hacknus/egui-file-dialog?branch=sort_by_metadata#6685bf25dbc2b29f2472d588539f7e5dba420a20" +dependencies = [ + "chrono", + "directories", + "dunce", + "egui", + "egui_extras", + "image-meta", + "indexmap 2.7.0", + "serde", + "sysinfo", +] + [[package]] name = "egui-phosphor" version = "0.8.0" @@ -1274,6 +1321,20 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_extras" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7a8198c088b1007108cb2d403bc99a5e370999b200db4f14559610d7330126" +dependencies = [ + "ahash", + "egui", + "enum-map", + "log", + "mime_guess2", + "profiling", +] + [[package]] name = "egui_glow" version = "0.30.0" @@ -1337,6 +1398,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", + "serde", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -1410,6 +1492,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "error-code" version = "3.3.1" @@ -1536,15 +1627,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "futures-channel" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" -dependencies = [ - "futures-core", -] - [[package]] name = "futures-core" version = "0.3.31" @@ -1819,6 +1901,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -2032,6 +2120,18 @@ dependencies = [ "png", ] +[[package]] +name = "image-meta" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5831d8b072b2162a3b4f143081b6dea66175e0d84b6fd5adaa9dc615c31ceaa" +dependencies = [ + "byteorder", + "skeptic", + "strum", + "thiserror", +] + [[package]] name = "immutable-chunkmap" version = "2.0.6" @@ -2059,6 +2159,7 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", + "serde", ] [[package]] @@ -2335,6 +2436,22 @@ dependencies = [ "paste", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2715,6 +2832,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.48" @@ -2896,12 +3019,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - [[package]] name = "powerfmt" version = "0.2.0" @@ -2962,6 +3079,17 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +[[package]] +name = "pulldown-cmark" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" +dependencies = [ + "bitflags 2.6.0", + "memchr", + "unicase", +] + [[package]] name = "quick-xml" version = "0.30.0" @@ -3053,6 +3181,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -3088,28 +3227,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" -[[package]] -name = "rfd" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8" -dependencies = [ - "ashpd", - "block2", - "js-sys", - "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "pollster", - "raw-window-handle", - "urlencoding", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "windows-sys 0.48.0", -] - [[package]] name = "ron" version = "0.8.1" @@ -3164,6 +3281,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" @@ -3209,6 +3332,9 @@ name = "semver" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -3268,6 +3394,7 @@ version = "0.3.2" dependencies = [ "csv", "eframe", + "egui-file-dialog", "egui-phosphor", "egui-theme-switch", "egui_logger", @@ -3277,7 +3404,6 @@ dependencies = [ "log", "preferences", "regex", - "rfd", "serde", "serialport", ] @@ -3334,6 +3460,21 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "skeptic" +version = "0.13.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d23b015676c90a0f01c197bfdc786c20342c73a0afdda9025adb0bc42940a8" +dependencies = [ + "bytecount", + "cargo_metadata", + "error-chain", + "glob", + "pulldown-cmark", + "tempfile", + "walkdir", +] + [[package]] name = "slab" version = "0.4.9" @@ -3446,6 +3587,28 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", +] + [[package]] name = "syn" version = "1.0.109" @@ -3479,6 +3642,17 @@ dependencies = [ "syn 2.0.91", ] +[[package]] +name = "sysinfo" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "948512566b1895f93b1592c7574baeb2de842f224f2aab158799ecadb8ebbb46" +dependencies = [ + "core-foundation-sys", + "libc", + "windows 0.57.0", +] + [[package]] name = "tempfile" version = "3.14.0" @@ -3721,6 +3895,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "unicase" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" + [[package]] name = "unicode-ident" version = "1.0.14" @@ -3754,15 +3934,8 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf16_iter" version = "1.0.5" @@ -4170,6 +4343,16 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows" version = "0.58.0" @@ -4189,19 +4372,42 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + [[package]] name = "windows-core" version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" dependencies = [ - "windows-implement", - "windows-interface", - "windows-result", + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", "windows-strings", "windows-targets 0.52.6", ] +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + [[package]] name = "windows-implement" version = "0.58.0" @@ -4213,6 +4419,17 @@ dependencies = [ "syn 2.0.91", ] +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.91", +] + [[package]] name = "windows-interface" version = "0.58.0" @@ -4224,6 +4441,15 @@ dependencies = [ "syn 2.0.91", ] +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.2.0" @@ -4239,7 +4465,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" dependencies = [ - "windows-result", + "windows-result 0.2.0", "windows-targets 0.52.6", ] @@ -4721,42 +4947,6 @@ dependencies = [ "zvariant 4.2.0", ] -[[package]] -name = "zbus" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb67eadba43784b6fb14857eba0d8fc518686d3ee537066eb6086dc318e2c8a1" -dependencies = [ - "async-broadcast 0.7.1", - "async-executor", - "async-fs 2.1.2", - "async-io 2.4.0", - "async-lock 3.4.0", - "async-process 2.3.0", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener 5.3.1", - "futures-core", - "futures-util", - "hex", - "nix 0.29.0", - "ordered-stream", - "serde", - "serde_repr", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.59.0", - "winnow 0.6.20", - "xdg-home", - "zbus_macros 5.2.0", - "zbus_names 4.1.0", - "zvariant 5.1.0", -] - [[package]] name = "zbus-lockstep" version = "0.4.4" @@ -4808,21 +4998,6 @@ dependencies = [ "zvariant_utils 2.1.0", ] -[[package]] -name = "zbus_macros" -version = "5.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d49ebc960ceb660f2abe40a5904da975de6986f2af0d7884b39eec6528c57" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.91", - "zbus_names 4.1.0", - "zvariant 5.1.0", - "zvariant_utils 3.0.2", -] - [[package]] name = "zbus_names" version = "2.6.1" @@ -4845,18 +5020,6 @@ dependencies = [ "zvariant 4.2.0", ] -[[package]] -name = "zbus_names" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" -dependencies = [ - "serde", - "static_assertions", - "winnow 0.6.20", - "zvariant 5.1.0", -] - [[package]] name = "zbus_xml" version = "4.0.0" @@ -4961,22 +5124,6 @@ dependencies = [ "zvariant_derive 4.2.0", ] -[[package]] -name = "zvariant" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "url", - "winnow 0.6.20", - "zvariant_derive 5.1.0", - "zvariant_utils 3.0.2", -] - [[package]] name = "zvariant_derive" version = "3.15.2" @@ -5003,19 +5150,6 @@ dependencies = [ "zvariant_utils 2.1.0", ] -[[package]] -name = "zvariant_derive" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916" -dependencies = [ - "proc-macro-crate 3.2.0", - "proc-macro2", - "quote", - "syn 2.0.91", - "zvariant_utils 3.0.2", -] - [[package]] name = "zvariant_utils" version = "1.0.1" @@ -5037,17 +5171,3 @@ dependencies = [ "quote", "syn 2.0.91", ] - -[[package]] -name = "zvariant_utils" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "static_assertions", - "syn 2.0.91", - "winnow 0.6.20", -] diff --git a/Cargo.toml b/Cargo.toml index e13d616..3a8d7fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,11 @@ egui_plot = "0.30.0" egui-phosphor = { version = "0.8.0" } egui-theme-switch = { version = "0.2.3", default-features = true } egui_logger = { git = "https://github.com/hacknus/egui_logger" } +egui-file-dialog = { version = "0.8.0", git = "https://github.com/hacknus/egui-file-dialog", branch = "sort_by_metadata", features = ["information_view"] } image = { version = "0.25", default-features = false, features = ["png"] } keepawake = { version = "0.5.1" } preferences = { version = "2.0.0" } regex = "1" -rfd = "0.15.0" serde = { version = "1.0", features = ["derive"] } serialport = { version = "4.6.1", features = ["serde"] } log = "0.4.22" diff --git a/src/gui.rs b/src/gui.rs index d29ad7b..1cae594 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -13,6 +13,8 @@ use eframe::egui::{ }; use eframe::{egui, Storage}; use egui::ThemePreference; +use egui_file_dialog::information_panel::InformationPanel; +use egui_file_dialog::FileDialog; use egui_plot::{log_grid_spacer, GridMark, Legend, Line, Plot, PlotPoint, PlotPoints}; use egui_theme_switch::ThemeSwitch; use preferences::Preferences; @@ -45,6 +47,13 @@ const SAVE_PLOT_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new( const CLEAR_PLOT_SHORTCUT: KeyboardShortcut = KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::X); +#[derive(Clone)] +pub enum FileDialogState { + Open, + Save, + SavePlot, + None, +} #[derive(PartialEq)] pub enum WindowFeedback { None, @@ -85,7 +94,7 @@ pub fn load_gui_settings() -> GuiSettingsContainer { let gui_settings = GuiSettingsContainer::default(); // save default settings if gui_settings.save(&APP_INFO, PREFS_KEY).is_err() { - println!("failed to save gui_settings"); + log::error!("failed to save gui_settings"); } gui_settings }) @@ -108,6 +117,9 @@ pub struct MyApp { picked_path: PathBuf, plot_location: Option, data: DataContainer, + file_dialog_state: FileDialogState, + file_dialog: FileDialog, + information_panel: InformationPanel, gui_conf: GuiSettingsContainer, device_lock: Arc>, devices_lock: Arc>>, @@ -134,6 +146,7 @@ pub struct MyApp { #[allow(clippy::too_many_arguments)] impl MyApp { pub fn new( + cc: &eframe::CreationContext, data_lock: Arc>, device_lock: Arc>, devices_lock: Arc>>, @@ -144,12 +157,54 @@ impl MyApp { send_tx: Sender, clear_tx: Sender, ) -> Self { + let mut file_dialog = FileDialog::default() + //.initial_directory(PathBuf::from("/path/to/app")) + .default_file_name("measurement.csv") + .default_size([600.0, 400.0]) + // .add_quick_access("Project", |s| { + // s.add_path("☆ Examples", "examples"); + // s.add_path("📷 Media", "media"); + // s.add_path("📂 Source", "src"); + // }) + .set_file_icon( + "🖹", + Arc::new(|path| path.extension().unwrap_or_default().to_ascii_lowercase() == "md"), + ) + .set_file_icon( + "", + Arc::new(|path| { + path.file_name().unwrap_or_default().to_ascii_lowercase() == ".gitignore" + }), + ) + .add_file_filter( + "CSV files", + Arc::new(|p| p.extension().unwrap_or_default().to_ascii_lowercase() == "csv"), + ); + // Load the persistent data of the file dialog. + // Alternatively, you can also use the `FileDialog::storage` builder method. + if let Some(storage) = cc.storage { + *file_dialog.storage_mut() = + eframe::get_value(storage, "file_dialog_storage").unwrap_or_default() + } + Self { connected_to_device: false, picked_path: PathBuf::new(), device: "".to_string(), old_device: "".to_string(), data: DataContainer::default(), + file_dialog_state: FileDialogState::None, + file_dialog, + information_panel: InformationPanel::default().add_file_preview("csv", |ui, item| { + ui.label("CSV preview:"); + if let Some(mut content) = item.content() { + egui::ScrollArea::vertical() + .max_height(ui.available_height()) + .show(ui, |ui| { + ui.add(egui::TextEdit::multiline(&mut content).code_editor()); + }); + } + }), connected_lock, device_lock, devices_lock, @@ -721,7 +776,7 @@ impl MyApp { }); }); } - fn draw_export_settings(&mut self, ctx: &egui::Context, ui: &mut Ui) { + fn draw_export_settings(&mut self, _ctx: &egui::Context, ui: &mut Ui) { egui::Grid::new("export_settings") .num_columns(2) .spacing(Vec2 { x: 10.0, y: 10.0 }) @@ -736,19 +791,9 @@ impl MyApp { .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_FILE_SHORTCUT)) { - if let Some(path) = rfd::FileDialog::new().save_file() { - self.picked_path = path; - self.picked_path.set_extension("csv"); - if let Err(e) = self.save_tx.send(FileOptions { - file_path: self.picked_path.clone(), - save_absolute_time: self.gui_conf.save_absolute_time, - save_raw_traffic: self.save_raw, - names: self.serial_devices.labels[self.device_idx].clone(), - }) { - log::error!("save_tx thread send failed: {:?}", e); - } - } - }; + self.file_dialog_state = FileDialogState::Save; + self.file_dialog.save_file(); + } if ui .button(egui::RichText::new(format!( @@ -759,7 +804,8 @@ impl MyApp { .clicked() || ui.input_mut(|i| i.consume_shortcut(&SAVE_PLOT_SHORTCUT)) { - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot(Default::default())); + self.file_dialog_state = FileDialogState::SavePlot; + self.file_dialog.save_file(); } ui.end_row(); ui.label("Save Raw Traffic"); @@ -1066,6 +1112,53 @@ impl MyApp { ui.collapsing("Debug logs:", |ui| { egui_logger::logger_ui().show(ui); }); + + match self.file_dialog_state { + FileDialogState::Open => { + if let Some(path) = self + .file_dialog + .update_with_right_panel_ui(ctx, &mut |ui, dia| { + self.information_panel.ui(ui, dia); + }) + .picked() + { + // self.picked_path = path.to_path_buf(); + // self.file_opened = true; + // self.file_dialog_state = FileDialogState::None; + // if let Err(e) = self.load_tx.send(self.picked_path.clone()) { + // log::error!("load_tx thread send failed: {:?}", e); + // } + } + } + FileDialogState::SavePlot => { + if let Some(path) = self.file_dialog.update(ctx).picked() { + self.picked_path = path.to_path_buf(); + self.file_dialog_state = FileDialogState::None; + self.picked_path.set_extension("png"); + + ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot( + Default::default(), + )); + } + } + FileDialogState::Save => { + if let Some(path) = self.file_dialog.update(ctx).picked() { + self.picked_path = path.to_path_buf(); + self.file_dialog_state = FileDialogState::None; + self.picked_path.set_extension("csv"); + + if let Err(e) = self.save_tx.send(FileOptions { + file_path: self.picked_path.clone(), + save_absolute_time: self.gui_conf.save_absolute_time, + save_raw_traffic: self.save_raw, + names: self.serial_devices.labels[self.device_idx].clone(), + }) { + log::error!("save_tx thread send failed: {:?}", e); + } + } + } + FileDialogState::None => {} + } }); }); } @@ -1110,32 +1203,28 @@ impl eframe::App for MyApp { }); if let (Some(screenshot), Some(plot_location)) = (screenshot, self.plot_location) { - if let Some(mut path) = rfd::FileDialog::new().save_file() { - path.set_extension("png"); - - // for a full size application, we should put this in a different thread, - // so that the GUI doesn't lag during saving - - let pixels_per_point = ctx.pixels_per_point(); - let plot = screenshot.region(&plot_location, Some(pixels_per_point)); - // save the plot to png - image::save_buffer( - &path, - plot.as_raw(), - plot.width() as u32, - plot.height() as u32, - image::ColorType::Rgba8, - ) - .unwrap(); - println!("Image saved to {path:?}."); - } + // for a full size application, we should put this in a different thread, + // so that the GUI doesn't lag during saving + + let pixels_per_point = ctx.pixels_per_point(); + let plot = screenshot.region(&plot_location, Some(pixels_per_point)); + // save the plot to png + image::save_buffer( + &self.picked_path, + plot.as_raw(), + plot.width() as u32, + plot.height() as u32, + image::ColorType::Rgba8, + ) + .unwrap(); + log::info!("Image saved to {:?}.", self.picked_path); } } fn save(&mut self, _storage: &mut dyn Storage) { save_serial_settings(&self.serial_devices); if let Err(err) = self.gui_conf.save(&APP_INFO, PREFS_KEY) { - println!("gui settings save failed: {:?}", err); + log::error!("gui settings save failed: {:?}", err); } } } diff --git a/src/main.rs b/src/main.rs index 49c3647..67f3385 100644 --- a/src/main.rs +++ b/src/main.rs @@ -138,7 +138,6 @@ fn main() { let serial_devices_lock = devices_lock.clone(); let serial_connected_lock = connected_lock.clone(); - println!("starting connection thread.."); let _serial_thread_handler = thread::spawn(|| { serial_thread( send_rx, @@ -151,7 +150,6 @@ fn main() { let main_data_lock = data_lock.clone(); - println!("starting main thread.."); let _main_thread_handler = thread::spawn(|| { main_thread(sync_tx, main_data_lock, raw_data_rx, save_rx, clear_rx); }); @@ -175,13 +173,13 @@ fn main() { if let Err(e) = eframe::run_native( "Serial Monitor", options, - Box::new(|_cc| { + Box::new(|ctx| { let mut fonts = egui::FontDefinitions::default(); egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular); - _cc.egui_ctx.set_fonts(fonts); - _cc.egui_ctx.set_visuals(Visuals::dark()); + ctx.egui_ctx.set_fonts(fonts); + ctx.egui_ctx.set_visuals(Visuals::dark()); - let repaint_signal = _cc.egui_ctx.clone(); + let repaint_signal = ctx.egui_ctx.clone(); thread::spawn(move || loop { if sync_rx.recv().is_ok() { repaint_signal.request_repaint(); @@ -189,6 +187,7 @@ fn main() { }); Ok(Box::new(MyApp::new( + ctx, gui_data_lock, gui_device_lock, gui_devices_lock, @@ -201,6 +200,6 @@ fn main() { ))) }), ) { - println!("error: {e:?}"); + log::error!("{e:?}"); } } From fd682237c85524d3e6d93a4172d27a5ae6790743 Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 13:19:13 +0100 Subject: [PATCH 02/10] load file prototype implemented --- src/gui.rs | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/src/gui.rs b/src/gui.rs index 1cae594..b35bc26 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -120,6 +120,7 @@ pub struct MyApp { file_dialog_state: FileDialogState, file_dialog: FileDialog, information_panel: InformationPanel, + file_opened: bool, gui_conf: GuiSettingsContainer, device_lock: Arc>, devices_lock: Arc>>, @@ -232,6 +233,7 @@ impl MyApp { show_warning_window: WindowFeedback::None, init: false, show_color_window: ColorWindow::NoShow, + file_opened: false, } } @@ -523,6 +525,9 @@ impl MyApp { let old_name = self.device.clone(); ui.horizontal(|ui| { + if self.file_opened { + ui.disable(); + } let dev_text = self.device.replace("/dev/tty.", ""); ui.horizontal(|ui| { if self.connected_to_device { @@ -775,6 +780,33 @@ impl MyApp { ); }); }); + ui.add_space(5.0); + ui.horizontal(|ui| { + if self.connected_to_device { + ui.disable(); + } + if ui + .button(egui::RichText::new(format!( + "{} Open file", + egui_phosphor::regular::FOLDER_OPEN + ))) + .on_hover_text("Load data from .csv") + .clicked() + { + self.file_dialog_state = FileDialogState::Open; + self.file_dialog.pick_file(); + } + if self.file_opened + && ui + .button(egui::RichText::new( + egui_phosphor::regular::X_SQUARE.to_string(), + )) + .on_hover_text("Close file.") + .clicked() + { + self.file_opened = false; + } + }); } fn draw_export_settings(&mut self, _ctx: &egui::Context, ui: &mut Ui) { egui::Grid::new("export_settings") @@ -1122,9 +1154,9 @@ impl MyApp { }) .picked() { - // self.picked_path = path.to_path_buf(); - // self.file_opened = true; - // self.file_dialog_state = FileDialogState::None; + self.picked_path = path.to_path_buf(); + self.file_opened = true; + self.file_dialog_state = FileDialogState::None; // if let Err(e) = self.load_tx.send(self.picked_path.clone()) { // log::error!("load_tx thread send failed: {:?}", e); // } From d49a431c46510772d9bf23f2880f71eb05859ed6 Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 13:22:26 +0100 Subject: [PATCH 03/10] implement egui loaders for image preview --- Cargo.lock | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 1 + 3 files changed, 287 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 5b7190a..a371f9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -861,6 +861,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.7" @@ -1032,6 +1038,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "data-url" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" + [[package]] name = "deranged" version = "0.3.11" @@ -1329,10 +1341,13 @@ checksum = "3d7a8198c088b1007108cb2d403bc99a5e370999b200db4f14559610d7330126" dependencies = [ "ahash", "egui", + "ehttp", "enum-map", + "image", "log", "mime_guess2", "profiling", + "resvg", ] [[package]] @@ -1376,6 +1391,20 @@ dependencies = [ "emath", ] +[[package]] +name = "ehttp" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876" +dependencies = [ + "document-features", + "js-sys", + "ureq", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "either" version = "1.13.0" @@ -1579,6 +1608,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "fnv" version = "1.0.7" @@ -1738,6 +1773,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gl_generator" version = "0.14.0" @@ -2116,6 +2161,8 @@ checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" dependencies = [ "bytemuck", "byteorder-lite", + "color_quant", + "gif", "num-traits", "png", ] @@ -2132,6 +2179,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "imagesize" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" + [[package]] name = "immutable-chunkmap" version = "2.0.6" @@ -2271,6 +2324,15 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "kurbo" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b" +dependencies = [ + "arrayvec", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -2913,6 +2975,12 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.7" @@ -3163,6 +3231,12 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" +[[package]] +name = "rctree" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3227,6 +3301,44 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "resvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4" +dependencies = [ + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "ron" version = "0.8.1" @@ -3239,6 +3351,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "roxmltree" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -3281,6 +3399,38 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.23.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.18" @@ -3397,6 +3547,7 @@ dependencies = [ "egui-file-dialog", "egui-phosphor", "egui-theme-switch", + "egui_extras", "egui_logger", "egui_plot", "image", @@ -3460,6 +3611,21 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simplecss" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" +dependencies = [ + "log", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + [[package]] name = "skeptic" version = "0.13.7" @@ -3554,6 +3720,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" @@ -3580,6 +3752,9 @@ name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] [[package]] name = "strsim" @@ -3609,6 +3784,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "svgtypes" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" +dependencies = [ + "kurbo", + "siphasher", +] + [[package]] name = "syn" version = "1.0.109" @@ -3737,6 +3928,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", + "png", "tiny-skia-path", ] @@ -3925,6 +4117,28 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "ureq" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" +dependencies = [ + "base64 0.22.1", + "flate2", + "log", + "once_cell", + "rustls", + "rustls-pki-types", + "url", + "webpki-roots", +] + [[package]] name = "url" version = "2.5.4" @@ -3936,6 +4150,50 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "usvg" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756" +dependencies = [ + "base64 0.21.7", + "log", + "pico-args", + "usvg-parser", + "usvg-tree", + "xmlwriter", +] + +[[package]] +name = "usvg-parser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc" +dependencies = [ + "data-url", + "flate2", + "imagesize", + "kurbo", + "log", + "roxmltree", + "simplecss", + "siphasher", + "svgtypes", + "usvg-tree", +] + +[[package]] +name = "usvg-tree" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3" +dependencies = [ + "rctree", + "strict-num", + "svgtypes", + "tiny-skia-path", +] + [[package]] name = "utf16_iter" version = "1.0.5" @@ -4190,6 +4448,21 @@ dependencies = [ "web-sys", ] +[[package]] +name = "webpki-roots" +version = "0.26.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "wgpu" version = "23.0.1" @@ -4844,6 +5117,12 @@ version = "0.8.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea8b391c9a790b496184c29f7f93b9ed5b16abb306c05415b68bcc16e4d06432" +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "yoke" version = "0.7.5" @@ -5075,6 +5354,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + [[package]] name = "zerovec" version = "0.10.4" diff --git a/Cargo.toml b/Cargo.toml index 3a8d7fd..756541e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ homepage = "https://github.com/hacknus/serial-monitor-rust" csv = "1.3" eframe = { version = "0.30.0", features = ["persistence"] } egui_plot = "0.30.0" +egui_extras = { version = "0.30.0", features = ["all_loaders"] } egui-phosphor = { version = "0.8.0" } egui-theme-switch = { version = "0.2.3", default-features = true } egui_logger = { git = "https://github.com/hacknus/egui_logger" } diff --git a/src/main.rs b/src/main.rs index 67f3385..a206afb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -178,6 +178,7 @@ fn main() { egui_phosphor::add_to_fonts(&mut fonts, egui_phosphor::Variant::Regular); ctx.egui_ctx.set_fonts(fonts); ctx.egui_ctx.set_visuals(Visuals::dark()); + egui_extras::install_image_loaders(&ctx.egui_ctx); let repaint_signal = ctx.egui_ctx.clone(); thread::spawn(move || loop { From f32be2b2c17a1be60b09578d42db74d2ab61bc8c Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 14:50:38 +0100 Subject: [PATCH 04/10] load file implemented in data_thread --- src/gui.rs | 15 ++++++-- src/io.rs | 53 +++++++++++++++++++++++++- src/main.rs | 107 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 141 insertions(+), 34 deletions(-) diff --git a/src/gui.rs b/src/gui.rs index b35bc26..004d4c2 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -127,6 +127,7 @@ pub struct MyApp { connected_lock: Arc>, data_lock: Arc>, save_tx: Sender, + load_tx: Sender, send_tx: Sender, clear_tx: Sender, history: Vec, @@ -155,6 +156,7 @@ impl MyApp { connected_lock: Arc>, gui_conf: GuiSettingsContainer, save_tx: Sender, + load_tx: Sender, send_tx: Sender, clear_tx: Sender, ) -> Self { @@ -214,6 +216,7 @@ impl MyApp { gui_conf, data_lock, save_tx, + load_tx, send_tx, clear_tx, plotting_range: usize::MAX, @@ -805,6 +808,8 @@ impl MyApp { .clicked() { self.file_opened = false; + let _ = self.load_tx.send(PathBuf::new()); + self.file_dialog_state = FileDialogState::None; } }); } @@ -1157,9 +1162,9 @@ impl MyApp { self.picked_path = path.to_path_buf(); self.file_opened = true; self.file_dialog_state = FileDialogState::None; - // if let Err(e) = self.load_tx.send(self.picked_path.clone()) { - // log::error!("load_tx thread send failed: {:?}", e); - // } + if let Err(e) = self.load_tx.send(self.picked_path.clone()) { + log::error!("load_tx thread send failed: {:?}", e); + } } } FileDialogState::SavePlot => { @@ -1189,7 +1194,9 @@ impl MyApp { } } } - FileDialogState::None => {} + FileDialogState::None => { + self.file_opened = false; + } } }); }); diff --git a/src/io.rs b/src/io.rs index 15fbddc..dcd9217 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::path::PathBuf; -use csv::WriterBuilder; +use csv::{ReaderBuilder, WriterBuilder}; use crate::DataContainer; @@ -14,6 +14,57 @@ pub struct FileOptions { pub names: Vec, } +pub fn open_from_csv( + data: &mut DataContainer, + csv_options: &mut FileOptions, +) -> Result<(), Box> { + let mut rdr = ReaderBuilder::new() + .has_headers(true) + .from_path(&csv_options.file_path)?; + + csv_options.names = rdr + .headers() + .unwrap() + .into_iter() + .skip(1) + .map(|s| s.to_string()) + .collect::>(); + + // Clear any existing data in the DataContainer + data.absolute_time.clear(); + data.time.clear(); + data.dataset = vec![vec![]; csv_options.names.len()]; + + // Read and parse each record in the CSV + for result in rdr.records() { + let record = result?; + + // Ensure the record has the correct number of fields + if record.len() != csv_options.names.len() + 1 { + return Err("CSV record does not match the expected number of columns".into()); + } + + // Parse the time field (first column) + let time_value = record.get(0).unwrap(); + if csv_options.save_absolute_time { + data.absolute_time.push(time_value.parse()?); + } else { + data.time.push(time_value.parse()?); + } + + // Parse the remaining columns and populate the dataset + for (i, value) in record.iter().skip(1).enumerate() { + if let Some(dataset_column) = data.dataset.get_mut(i) { + dataset_column.push(value.parse()?); + } else { + return Err("Unexpected number of data columns in the CSV".into()); + } + } + } + + Ok(()) +} + pub fn save_to_csv(data: &DataContainer, csv_options: &FileOptions) -> Result<(), Box> { let mut wtr = WriterBuilder::new() .has_headers(false) diff --git a/src/main.rs b/src/main.rs index a206afb..eb4477b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ extern crate preferences; extern crate serde; use std::cmp::max; +use std::path::PathBuf; use std::sync::mpsc::{Receiver, Sender}; use std::sync::{mpsc, Arc, RwLock}; use std::thread; @@ -13,7 +14,7 @@ use std::time::Duration; use crate::data::{DataContainer, Packet}; use crate::gui::{load_gui_settings, MyApp, RIGHT_PANEL_WIDTH}; -use crate::io::{save_to_csv, FileOptions}; +use crate::io::{open_from_csv, save_to_csv, FileOptions}; use crate::serial::{load_serial_settings, serial_thread, Device}; use eframe::egui::{vec2, ViewportBuilder, Visuals}; use eframe::{egui, icon_data}; @@ -51,11 +52,15 @@ fn main_thread( data_lock: Arc>, raw_data_rx: Receiver, save_rx: Receiver, + load_rx: Receiver, clear_rx: Receiver, ) { // reads data from mutex, samples and saves if needed let mut data = DataContainer::default(); let mut failed_format_counter = 0; + + let mut file_opened = false; + loop { if let Ok(cl) = clear_rx.recv_timeout(Duration::from_millis(1)) { if cl { @@ -63,39 +68,74 @@ fn main_thread( failed_format_counter = 0; } } - - if let Ok(packet) = raw_data_rx.recv_timeout(Duration::from_millis(1)) { - if !packet.payload.is_empty() { - sync_tx.send(true).expect("unable to send sync tx"); - data.raw_traffic.push(packet.clone()); - let split_data = split(&packet.payload); - if data.dataset.is_empty() || failed_format_counter > 10 { - // resetting dataset - data.dataset = vec![vec![]; max(split_data.len(), 1)]; - failed_format_counter = 0; - // println!("resetting dataset. split length = {}, length data.dataset = {}", split_data.len(), data.dataset.len()); - } else if split_data.len() == data.dataset.len() { - // appending data - for (i, set) in data.dataset.iter_mut().enumerate() { - set.push(split_data[i]); - failed_format_counter = 0; - } - data.time.push(packet.relative_time); - data.absolute_time.push(packet.absolute_time); - if data.time.len() != data.dataset[0].len() { + if !file_opened { + if let Ok(packet) = raw_data_rx.recv_timeout(Duration::from_millis(1)) { + if !packet.payload.is_empty() { + sync_tx.send(true).expect("unable to send sync tx"); + data.raw_traffic.push(packet.clone()); + let split_data = split(&packet.payload); + if data.dataset.is_empty() || failed_format_counter > 10 { // resetting dataset - data.time = vec![]; data.dataset = vec![vec![]; max(split_data.len(), 1)]; + failed_format_counter = 0; + // println!("resetting dataset. split length = {}, length data.dataset = {}", split_data.len(), data.dataset.len()); + } else if split_data.len() == data.dataset.len() { + // appending data + for (i, set) in data.dataset.iter_mut().enumerate() { + set.push(split_data[i]); + failed_format_counter = 0; + } + data.time.push(packet.relative_time); + data.absolute_time.push(packet.absolute_time); + if data.time.len() != data.dataset[0].len() { + // resetting dataset + data.time = vec![]; + data.dataset = vec![vec![]; max(split_data.len(), 1)]; + } + } else { + // not same length + failed_format_counter += 1; + // println!("not same length in main! length split_data = {}, length data.dataset = {}", split_data.len(), data.dataset.len()) } - } else { - // not same length - failed_format_counter += 1; - // println!("not same length in main! length split_data = {}, length data.dataset = {}", split_data.len(), data.dataset.len()) } - if let Ok(mut write_guard) = data_lock.write() { - *write_guard = data.clone(); + } + } + if let Ok(fp) = load_rx.recv_timeout(Duration::from_millis(10)) { + if let Some(file_ending) = fp.extension() { + match file_ending.to_str().unwrap() { + "csv" => { + file_opened = true; + let mut file_options = FileOptions { + file_path: fp.clone(), + save_absolute_time: false, + save_raw_traffic: false, + names: vec![], + }; + match open_from_csv(&mut data, &mut file_options) { + Ok(_) => { + log::info!("opened {:?}", fp); + } + Err(err) => { + file_opened = false; + log::error!("failed opening {:?}: {:?}", fp, err); + } + }; + } + _ => { + file_opened = false; + log::error!("file not supported: {:?} \n Close the file to connect to a spectrometer or open another file.", fp); + continue; + } } + } else { + file_opened = false; } + } else { + file_opened = false; + } + + if let Ok(mut write_guard) = data_lock.write() { + *write_guard = data.clone(); } if let Ok(csv_options) = save_rx.recv_timeout(Duration::from_millis(1)) { @@ -129,6 +169,7 @@ fn main() { let connected_lock = Arc::new(RwLock::new(false)); let (save_tx, save_rx): (Sender, Receiver) = mpsc::channel(); + let (load_tx, load_rx): (Sender, Receiver) = mpsc::channel(); let (send_tx, send_rx): (Sender, Receiver) = mpsc::channel(); let (clear_tx, clear_rx): (Sender, Receiver) = mpsc::channel(); let (raw_data_tx, raw_data_rx): (Sender, Receiver) = mpsc::channel(); @@ -151,7 +192,14 @@ fn main() { let main_data_lock = data_lock.clone(); let _main_thread_handler = thread::spawn(|| { - main_thread(sync_tx, main_data_lock, raw_data_rx, save_rx, clear_rx); + main_thread( + sync_tx, + main_data_lock, + raw_data_rx, + save_rx, + load_rx, + clear_rx, + ); }); let options = eframe::NativeOptions { @@ -196,6 +244,7 @@ fn main() { gui_connected_lock, gui_settings, save_tx, + load_tx, send_tx, clear_tx, ))) From 4707289633f33d8765ac121145a079c3beed71bd Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 14:53:51 +0100 Subject: [PATCH 05/10] changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1149e..dee7eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to the `Serial Monitor` crate will be documented in this fil # Unreleased 0.3.x +* implement `egui-file-dialog` and the feature to open `.csv` files. + # 0.3.2 * fixed display of only one dataset bug From ce8e7995cacd6a02362921670712ebceeb7e415b Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 15:04:23 +0100 Subject: [PATCH 06/10] wayland and x11 features for linux --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 756541e..b33452d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://github.com/hacknus/serial-monitor-rust" [dependencies] csv = "1.3" -eframe = { version = "0.30.0", features = ["persistence"] } +eframe = { version = "0.30.0", features = ["persistence", "wayland", "x11"] } egui_plot = "0.30.0" egui_extras = { version = "0.30.0", features = ["all_loaders"] } egui-phosphor = { version = "0.8.0" } From acd989f995ad6fa32e9f9cfaa52cf6ddfb92cf61 Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 15:24:22 +0100 Subject: [PATCH 07/10] time as f64 to allow for all kinds of input files (not just integer time scales) --- src/data.rs | 12 ++++++------ src/gui.rs | 4 +--- src/serial.rs | 8 ++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/data.rs b/src/data.rs index aa0ddc7..b35cdf6 100644 --- a/src/data.rs +++ b/src/data.rs @@ -25,8 +25,8 @@ pub fn get_epoch_ms() -> u128 { #[derive(Clone, Debug)] pub struct Packet { - pub relative_time: u128, - pub absolute_time: u128, + pub relative_time: f64, + pub absolute_time: f64, pub direction: SerialDirection, pub payload: String, } @@ -34,8 +34,8 @@ pub struct Packet { impl Default for Packet { fn default() -> Packet { Packet { - relative_time: 0, - absolute_time: get_epoch_ms(), + relative_time: 0.0, + absolute_time: get_epoch_ms() as f64, direction: SerialDirection::Send, payload: "".to_string(), } @@ -44,8 +44,8 @@ impl Default for Packet { #[derive(Clone, Debug)] pub struct DataContainer { - pub time: Vec, - pub absolute_time: Vec, + pub time: Vec, + pub absolute_time: Vec, pub dataset: Vec>, pub raw_traffic: Vec, } diff --git a/src/gui.rs b/src/gui.rs index 004d4c2..213e98d 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1194,9 +1194,7 @@ impl MyApp { } } } - FileDialogState::None => { - self.file_opened = false; - } + FileDialogState::None => {} } }); }); diff --git a/src/serial.rs b/src/serial.rs index ce5a74e..59531b4 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -252,8 +252,8 @@ fn perform_writes( } let packet = Packet { - relative_time: Instant::now().duration_since(t_zero).as_millis(), - absolute_time: get_epoch_ms(), + relative_time: Instant::now().duration_since(t_zero).as_millis() as f64, + absolute_time: get_epoch_ms() as f64, direction: SerialDirection::Send, payload: cmd, }; @@ -274,8 +274,8 @@ fn perform_reads( let delimiter = if buf.contains("\r\n") { "\r\n" } else { "\0\0" }; buf.split_terminator(delimiter).for_each(|s| { let packet = Packet { - relative_time: Instant::now().duration_since(t_zero).as_millis(), - absolute_time: get_epoch_ms(), + relative_time: Instant::now().duration_since(t_zero).as_millis() as f64, + absolute_time: get_epoch_ms() as f64, direction: SerialDirection::Receive, payload: s.to_owned(), }; From 994d1d0d1d39dfd041d77687dbf9b07fff130dc1 Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 16:13:17 +0100 Subject: [PATCH 08/10] println to log::error --- src/main.rs | 4 ++-- src/serial.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index eb4477b..8edd2e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -78,7 +78,7 @@ fn main_thread( // resetting dataset data.dataset = vec![vec![]; max(split_data.len(), 1)]; failed_format_counter = 0; - // println!("resetting dataset. split length = {}, length data.dataset = {}", split_data.len(), data.dataset.len()); + // log::error!("resetting dataset. split length = {}, length data.dataset = {}", split_data.len(), data.dataset.len()); } else if split_data.len() == data.dataset.len() { // appending data for (i, set) in data.dataset.iter_mut().enumerate() { @@ -95,7 +95,7 @@ fn main_thread( } else { // not same length failed_format_counter += 1; - // println!("not same length in main! length split_data = {}, length data.dataset = {}", split_data.len(), data.dataset.len()) + // log::error!("not same length in main! length split_data = {}, length data.dataset = {}", split_data.len(), data.dataset.len()) } } } diff --git a/src/serial.rs b/src/serial.rs index 59531b4..ed4ee1b 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -47,14 +47,14 @@ pub fn load_serial_settings() -> SerialDevices { pub fn save_serial_settings(serial_configs: &SerialDevices) { if serial_configs.save(&APP_INFO, PREFS_KEY_SERIAL).is_err() { - println!("failed to save gui_settings"); + log::error!("failed to save gui_settings"); } } pub fn clear_serial_settings() { let serial_configs = SerialDevices::default(); if serial_configs.save(&APP_INFO, PREFS_KEY_SERIAL).is_err() { - println!("failed to clear gui_settings"); + log::error!("failed to clear gui_settings"); } } @@ -247,7 +247,7 @@ fn perform_writes( ) { if let Ok(cmd) = send_rx.recv_timeout(Duration::from_millis(1)) { if let Err(e) = serial_write(port, cmd.as_bytes()) { - println!("Error sending command: {e}"); + log::error!("Error sending command: {e}"); return; } @@ -285,7 +285,7 @@ fn perform_reads( // Timeout is ok, just means there is no data to read Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => {} Err(e) => { - println!("Error reading: {:?}", e); + log::error!("Error reading: {:?}", e); } } } From 8727a301d17a6a2b18aa28482e2f08b568e4f57e Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 16:34:52 +0100 Subject: [PATCH 09/10] implement labels of loaded files --- src/data.rs | 2 ++ src/gui.rs | 19 +++++++++++++++++-- src/io.rs | 2 ++ src/main.rs | 9 +++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/data.rs b/src/data.rs index b35cdf6..20cffe4 100644 --- a/src/data.rs +++ b/src/data.rs @@ -48,6 +48,7 @@ pub struct DataContainer { pub absolute_time: Vec, pub dataset: Vec>, pub raw_traffic: Vec, + pub loaded_from_file: bool, } impl Default for DataContainer { @@ -57,6 +58,7 @@ impl Default for DataContainer { absolute_time: vec![], dataset: vec![vec![]], raw_traffic: vec![], + loaded_from_file: false, } } } diff --git a/src/gui.rs b/src/gui.rs index 213e98d..da27ee6 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -2,7 +2,7 @@ use core::f32; use std::cmp::max; use std::ops::RangeInclusive; use std::path::PathBuf; -use std::sync::mpsc::Sender; +use std::sync::mpsc::{Receiver, Sender}; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -128,6 +128,7 @@ pub struct MyApp { data_lock: Arc>, save_tx: Sender, load_tx: Sender, + load_names_rx: Receiver>, send_tx: Sender, clear_tx: Sender, history: Vec, @@ -157,6 +158,7 @@ impl MyApp { gui_conf: GuiSettingsContainer, save_tx: Sender, load_tx: Sender, + load_names_rx: Receiver>, send_tx: Sender, clear_tx: Sender, ) -> Self { @@ -217,6 +219,7 @@ impl MyApp { data_lock, save_tx, load_tx, + load_names_rx, send_tx, clear_tx, plotting_range: usize::MAX, @@ -317,8 +320,20 @@ impl MyApp { if let Ok(read_guard) = self.data_lock.read() { self.data = read_guard.clone(); } + + if self.data.loaded_from_file && self.file_opened { + if let Ok(labels) = + self.load_names_rx.recv_timeout(Duration::from_millis(10)) + { + self.labels = labels; + self.colors = (0..max(self.labels.len(), 1)) + .map(|i| COLORS[i % COLORS.len()]) + .collect(); + self.color_vals = (0..max(self.labels.len(), 1)).map(|_| 0.0).collect(); + } + } if self.serial_devices.number_of_plots[self.device_idx] > 0 { - if self.data.dataset.len() != self.labels.len() { + if self.data.dataset.len() != self.labels.len() && !self.file_opened { self.labels = (0..max(self.data.dataset.len(), 1)) .map(|i| format!("Column {i}")) .collect(); diff --git a/src/io.rs b/src/io.rs index dcd9217..46e25a9 100644 --- a/src/io.rs +++ b/src/io.rs @@ -62,6 +62,8 @@ pub fn open_from_csv( } } + data.loaded_from_file = true; + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 8edd2e4..ae98190 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,6 +53,7 @@ fn main_thread( raw_data_rx: Receiver, save_rx: Receiver, load_rx: Receiver, + load_names_tx: Sender>, clear_rx: Receiver, ) { // reads data from mutex, samples and saves if needed @@ -70,6 +71,7 @@ fn main_thread( } if !file_opened { if let Ok(packet) = raw_data_rx.recv_timeout(Duration::from_millis(1)) { + data.loaded_from_file = false; if !packet.payload.is_empty() { sync_tx.send(true).expect("unable to send sync tx"); data.raw_traffic.push(packet.clone()); @@ -114,6 +116,9 @@ fn main_thread( match open_from_csv(&mut data, &mut file_options) { Ok(_) => { log::info!("opened {:?}", fp); + load_names_tx + .send(file_options.names) + .expect("unable to send names on channel after loading"); } Err(err) => { file_opened = false; @@ -170,6 +175,8 @@ fn main() { let (save_tx, save_rx): (Sender, Receiver) = mpsc::channel(); let (load_tx, load_rx): (Sender, Receiver) = mpsc::channel(); + let (loaded_names_tx, loaded_names_rx): (Sender>, Receiver>) = + mpsc::channel(); let (send_tx, send_rx): (Sender, Receiver) = mpsc::channel(); let (clear_tx, clear_rx): (Sender, Receiver) = mpsc::channel(); let (raw_data_tx, raw_data_rx): (Sender, Receiver) = mpsc::channel(); @@ -198,6 +205,7 @@ fn main() { raw_data_rx, save_rx, load_rx, + loaded_names_tx, clear_rx, ); }); @@ -245,6 +253,7 @@ fn main() { gui_settings, save_tx, load_tx, + loaded_names_rx, send_tx, clear_tx, ))) From aecb8b7e6e4df02abd1f25ee8320532d5d6544ad Mon Sep 17 00:00:00 2001 From: hacknus Date: Sun, 22 Dec 2024 16:48:00 +0100 Subject: [PATCH 10/10] fix labels for saving error --- src/gui.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui.rs b/src/gui.rs index da27ee6..1640808 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -1203,7 +1203,7 @@ impl MyApp { file_path: self.picked_path.clone(), save_absolute_time: self.gui_conf.save_absolute_time, save_raw_traffic: self.save_raw, - names: self.serial_devices.labels[self.device_idx].clone(), + names: self.labels.clone(), }) { log::error!("save_tx thread send failed: {:?}", e); }