From 298667b5e1fdc122c30eba92470acc28cbf3e178 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Fri, 30 Aug 2024 20:35:55 +0700
Subject: [PATCH 01/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/README.md b/README.md
index cf6301f..8e1d5e4 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
+
+
Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`.
---
From 447e48b993a1560326281814509ca95339359107 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Fri, 30 Aug 2024 20:37:57 +0700
Subject: [PATCH 02/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8e1d5e4..9101e1e 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`.
From 823608aa33be85344642e58bbc57998d6b3c2ff1 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sat, 31 Aug 2024 09:46:56 +0700
Subject: [PATCH 03/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index a920820..957d60b 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -328,6 +328,37 @@ def is_binary_file(file_path):
except Exception as e:
return False
+def validate_file(file_path, max_size_mb=100, max_read_time=2):
+ try:
+ # Periksa ukuran file
+ file_size = os.path.getsize(file_path)
+ if file_size > max_size_mb * 1024 * 1024:
+ return False
+
+ # Mulai waktu pembacaan
+ start_time = time.time()
+
+ # Baca file
+ with open(file_path, "rb+",
+ encoding=sys.getfilesystemencoding()) as f:
+ # Baca bagian pertama file untuk memeriksa apakah file biner
+ first_bytes = f.read(1024)
+ if b"\x00" in first_bytes:
+ return False
+
+ # Lanjutkan membaca file
+ while f.read(1024):
+ # Periksa waktu yang telah digunakan untuk membaca
+ elapsed_time = time.time() - start_time
+ if elapsed_time > max_read_time:
+ return False
+
+ # Jika semua pemeriksaan lolos, file valid
+ return True
+
+ except Exception as e:
+ return False
+
def check_class_in_package(package_name, class_name):
try:
From cc1a4c674224be74a508afe62f586301144c2f18 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sat, 31 Aug 2024 09:52:37 +0700
Subject: [PATCH 04/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 46a5037..fb10983 100644
--- a/supernano.py
+++ b/supernano.py
@@ -12,6 +12,7 @@
from libs.timeout import timeout_v2, timeout_v1
from libs.filemanager import (
StreamFile,
+ validate_file,
ModuleInspector,
create_file_or_folder,
resolve_relative_path_v2,
@@ -27,6 +28,7 @@
from .timeout import timeout_v2, timeout_v1
from .filemanager import (
StreamFile,
+ validate_file,
ModuleInspector,
create_file_or_folder,
resolve_relative_path_v2,
@@ -41,6 +43,7 @@
from timeout import timeout_v2, timeout_v1
from filemanager import (
StreamFile,
+ validate_file,
ModuleInspector,
create_file_or_folder,
resolve_relative_path_v2,
@@ -659,7 +662,9 @@ def open_file(self, button, file_name):
else:
self.status_msg_footer_text.set_text("Folder access denied!")
else:
- if validate_folder(os.path.dirname(file_path)):
+ if validate_folder(os.path.dirname(file_path)) and validate_file(
+ file_path, os.path.getsize(file_path) or 20, 6
+ ):
try:
with open(
file_path, "r+", encoding=sys.getfilesystemencoding()
From c20d800d01201b9b1b54c83c41a51e21b4d605f5 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sat, 31 Aug 2024 10:02:14 +0700
Subject: [PATCH 05/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 50 +++++++++++++++++++++------------------------
1 file changed, 23 insertions(+), 27 deletions(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 957d60b..37d4e32 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -328,34 +328,30 @@ def is_binary_file(file_path):
except Exception as e:
return False
-def validate_file(file_path, max_size_mb=100, max_read_time=2):
- try:
- # Periksa ukuran file
- file_size = os.path.getsize(file_path)
- if file_size > max_size_mb * 1024 * 1024:
- return False
-
- # Mulai waktu pembacaan
- start_time = time.time()
-
- # Baca file
- with open(file_path, "rb+",
- encoding=sys.getfilesystemencoding()) as f:
- # Baca bagian pertama file untuk memeriksa apakah file biner
- first_bytes = f.read(1024)
- if b"\x00" in first_bytes:
- return False
-
- # Lanjutkan membaca file
- while f.read(1024):
- # Periksa waktu yang telah digunakan untuk membaca
- elapsed_time = time.time() - start_time
- if elapsed_time > max_read_time:
- return False
-
- # Jika semua pemeriksaan lolos, file valid
- return True
+def is_binary_file(file_path):
+ """
+ Menentukan apakah file adalah file biner atau bukan.
+
+ Args:
+ file_path (str): Path ke file yang akan diperiksa.
+ Returns:
+ bool: True jika file adalah file biner, False jika bukan.
+ """
+ try:
+ with open(file_path, "rb+") as file:
+ chunk = file.read(1024) # Membaca bagian pertama file (1KB)
+ # Cek apakah file memiliki karakter yang tidak biasa untuk teks
+ if b"\0" in chunk: # Null byte adalah indikator umum dari file biner
+ return True
+ # Cek apakah file sebagian besar berisi karakter teks (misalnya ASCII)
+ text_chars = b"".join([bytes((i,)) for i in range(32, 127)]) + b"\n\r\t\b"
+ non_text_chars = chunk.translate(None, text_chars)
+ if (
+ len(non_text_chars) / len(chunk) > 0.30
+ ): # Jika lebih dari 30% karakter non-teks
+ return True
+ return False
except Exception as e:
return False
From 2c9d848499e1358dbf12f9bd5413d34a4c240fc4 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sat, 31 Aug 2024 10:05:57 +0700
Subject: [PATCH 06/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 49 ++++++++++++++++++++++++---------------------
1 file changed, 26 insertions(+), 23 deletions(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 37d4e32..19d39c0 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -328,30 +328,33 @@ def is_binary_file(file_path):
except Exception as e:
return False
-def is_binary_file(file_path):
- """
- Menentukan apakah file adalah file biner atau bukan.
-
- Args:
- file_path (str): Path ke file yang akan diperiksa.
-
- Returns:
- bool: True jika file adalah file biner, False jika bukan.
- """
+def validate_file(file_path, max_size_mb=100, max_read_time=2):
try:
- with open(file_path, "rb+") as file:
- chunk = file.read(1024) # Membaca bagian pertama file (1KB)
- # Cek apakah file memiliki karakter yang tidak biasa untuk teks
- if b"\0" in chunk: # Null byte adalah indikator umum dari file biner
- return True
- # Cek apakah file sebagian besar berisi karakter teks (misalnya ASCII)
- text_chars = b"".join([bytes((i,)) for i in range(32, 127)]) + b"\n\r\t\b"
- non_text_chars = chunk.translate(None, text_chars)
- if (
- len(non_text_chars) / len(chunk) > 0.30
- ): # Jika lebih dari 30% karakter non-teks
- return True
- return False
+ # Periksa ukuran file
+ file_size = os.path.getsize(file_path)
+ if file_size > max_size_mb * 1024 * 1024:
+ return False
+
+ # Mulai waktu pembacaan
+ start_time = time.time()
+
+ # Baca file
+ with open(file_path, "rb") as f:
+ # Baca bagian pertama file untuk memeriksa apakah file biner
+ first_bytes = f.read(1024)
+ if b"\x00" in first_bytes:
+ return False
+
+ # Lanjutkan membaca file
+ while f.read(1024):
+ # Periksa waktu yang telah digunakan untuk membaca
+ elapsed_time = time.time() - start_time
+ if elapsed_time > max_read_time:
+ return False
+
+ # Jika semua pemeriksaan lolos, file valid
+ return True
+
except Exception as e:
return False
From 69899da24e07f2adb8b424d4ccc4b1e966f4122e Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 14:23:13 +0700
Subject: [PATCH 07/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/supernano.py b/supernano.py
index fb10983..1291512 100644
--- a/supernano.py
+++ b/supernano.py
@@ -123,8 +123,8 @@ def parse_args():
type=str,
help="Target file or directory to edit.",
)
- args = vars(parser.parse_args())
- path = args.get("path", ".").strip().replace("\\", "/")
+ args = parser.parse_args()
+ path = resolve_relative_path(args.path, "")
if os.path.exists(path):
if validate_folder(path=path):
pass
From a72e5b0ead25ac717d66aad88db321146cb8f404 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 14:29:27 +0700
Subject: [PATCH 08/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 1291512..41f40f4 100644
--- a/supernano.py
+++ b/supernano.py
@@ -124,7 +124,7 @@ def parse_args():
help="Target file or directory to edit.",
)
args = parser.parse_args()
- path = resolve_relative_path(args.path, "")
+ path = resolve_relative_path(args.path, "") or "."
if os.path.exists(path):
if validate_folder(path=path):
pass
From 779f87e73b89de41f79071603a0e4d125d422301 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 21:34:59 +0700
Subject: [PATCH 09/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 41f40f4..273b325 100644
--- a/supernano.py
+++ b/supernano.py
@@ -934,7 +934,10 @@ def main(path: str):
app = SuperNano(start_path=path)
app.run()
-
+if '__file__' in globals():
+ print("This script should not be imported. Run it directly.")
+ sys.exit()
+
if __name__ == "__main__":
set_low_priority(os.getpid())
#########mendapatkan process terbaik tanpa membebani ram dan cpu
From 490d096983d3772ce6309409aa64c4674b109c79 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 22:36:20 +0700
Subject: [PATCH 10/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 117 +++++++++++++++++++++++++++---------------------------
1 file changed, 59 insertions(+), 58 deletions(-)
diff --git a/README.md b/README.md
index 9101e1e..258e43e 100644
--- a/README.md
+++ b/README.md
@@ -1,60 +1,60 @@
-Berikut adalah dokumentasi untuk script `SuperNano`, sebuah text editor berbasis console yang menggunakan Python 3.6 ke atas dan pustaka `urwid[curses]`.
+Here is the documentation for the `SuperNano` script, a powerful console-based text editor specialized for Windows 8, 10, 11 platforms.
---
-# Dokumentasi SuperNano
-
-## Deskripsi
-`SuperNano` adalah sebuah text editor berbasis console yang dikembangkan menggunakan Python dan pustaka `urwid[curses]`. Aplikasi ini dirancang untuk memberikan pengguna kemampuan untuk mengedit teks, mengelola file, dan melakukan inspeksi modul Python langsung dari antarmuka berbasis console. `SuperNano` mendukung beberapa fitur seperti undo-redo, clipboard (copy-paste), pencarian file, dan inspeksi modul Python.
-
-## Fitur Utama
-- **Text Editing**: Editor teks dengan dukungan multiline, undo-redo, copy-paste, dan penyimpanan file.
-- **File Management**: Memungkinkan navigasi direktori, membuka dan menyimpan file, serta membuat dan menghapus file.
-- **Module Inspection**: Fitur untuk melakukan inspeksi modul Python, menampilkan informasi tentang variabel global, kelas, dan fungsi yang ada di dalam modul.
-
-
-## Kelas dan Metode
-
-### 1. `SuperNano`
-`SuperNano` adalah kelas utama yang mengatur seluruh aplikasi, termasuk inisialisasi, pembuatan menu, dan manajemen UI.
-
-#### Atribut:
-- **current_path**: Menyimpan path direktori saat ini.
-- **current_file_name**: Menyimpan nama file yang sedang dibuka.
-- **undo_stack**, **redo_stack**: Stack yang digunakan untuk menyimpan state teks guna mendukung fitur undo-redo.
-- **overlay**: Widget yang digunakan untuk menampilkan popup.
-- **modulepython**: Objek dari `ModuleInspector` yang digunakan untuk inspeksi modul Python.
-- **loop**: Objek `urwid.MainLoop` yang menangani event loop aplikasi.
-- **loading_alarm**, **system_alarm**: Alarm untuk mengatur timing penggantian layout dan memonitor sistem.
-
-#### Metode:
-- **`__init__(self, start_path=".")`**: Inisialisasi kelas, menyiapkan path awal, widget, dan memulai event loop.
-- **`load_main_menu(self)`**: Menyiapkan dan menampilkan menu utama setelah periode loading.
-- **`switch_to_secondary_layout(self)`**: Mengubah layout aplikasi ke menu utama.
-- **`setup_main_menu(self)`**: Mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional.
-- **`create_modules_menus(self, listmodulename)`**: Membuat tombol untuk setiap modul yang ada di `sys.path`.
-- **`inspect_module(self, button, module_name)`**: Menampilkan hasil inspeksi modul dalam footer.
-- **`setup_popup(self, options, title, descrip="")`**: Menyiapkan konten dan layout untuk menu popup.
-- **`show_popup(self, title, descrip, menus)`**: Menampilkan popup menu dengan judul, deskripsi, dan opsi yang diberikan.
-- **`close_popup(self, button)`**: Menutup popup dan mengembalikan tampilan ke layout utama.
-- **`get_file_list(self)`**: Mengambil daftar file dan direktori di path saat ini.
-- **`handle_input(self, key)`**: Menangani input keyboard untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy-paste, dan refresh UI.
-- **`get_current_edit(self)`**: Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit).
-- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Mengatur fokus pada widget edit berdasarkan klik dan indeks.
-- **`copy_text_to_clipboard(self)`**: Menyalin teks dari widget edit yang sedang aktif ke clipboard.
-- **`paste_text_from_clipboard(self)`**: Menempelkan teks dari clipboard ke widget edit yang sedang aktif.
+# SuperNano Documentation
+
+## Description
+`SuperNano` is a console-based text editor developed using Python and the `urwid[curses]` library. It is designed to give users the ability to edit text, manage files, and inspect Python modules directly from a console-based interface. SuperNano supports several features such as undo-redo, clipboard (copy-paste), file search, and Python module inspection.
+
+## Key Features
+- **Text Editing**: Text editor with multiline support, undo-redo, copy-paste, and file saving.
+- **File Management**: Allows directory navigation, opening and saving files, and creating and deleting files.
+- Module Inspection**: Features for inspecting Python modules, displaying information about global variables, classes, and functions within the module.
+
+
+## Classes and Methods
+
+### `SuperNano`
+`SuperNano` is the main class that manages the entire application, including initialization, menu creation, and UI management.
+
+#### Attributes:
+- **current_path**: Stores the current directory path.
+- **current_file_name**: Stores the name of the current file.
+- **undo_stack**, **redo_stack**: Stack used to store text state to support undo-redo feature.
+- **overlay**: Widgets used to display popups.
+- **modulePython**: Object of `ModuleInspector` used for Python module inspection.
+- **loop**: The `urwid.MainLoop` object that handles application loop events.
+- **loading_alarm**, **system_alarm**: Alarms for timing layout changes and monitoring the system.
+
+#### Methods:
+- **`__init__(self, start_path=“.”)`**: Initialize the class, set up the start path, widgets, and start the event loop.
+- **`load_main_menu(self)`**: Set up and display the main menu after the loading period.
+- **`switch_to_secondary_layout(self)`**: Changes the application layout to the main menu.
+- **`setup_main_menu(self)`**: Set up widgets for the main menu, including the file list, text editor, and functional buttons.
+- **`create_modules_menus(self, listmodulename)`**: Creates a button for each module in `sys.path`.
+- **`inspect_module(self, button, module_name)`**: Displays module inspection results in the footer.
+- **`setup_popup(self, options, title, descrip=“”)`**: Sets up the content and layout for the popup menu.
+- **`show_popup(self, title, descrip, menus)`**: Displays the popup menu with the given title, description, and options.
+- **`close_popup(self, button)`**: Closes the popup and returns to the main layout.
+- **`get_file_list(self)`**: Retrieve a list of files and directories in the current path.
+- **`handle_input(self, key)`**: Handles keyboard input for various actions such as exit, save, delete, undo, redo, copy-paste, and UI refresh.
+- **`get_current_edit(self)`**: Returns the currently focused edit widget (text editor or search edit).
+- **`set_focus_on_click(self, widget, new_edit_text, index)`**: Sets the focus on the edit widget based on click and index.
+- **`copy_text_to_clipboard(self)`**: Copies the text from the current edit widget to the clipboard.
+- **`paste_text_from_clipboard(self)`**: Paste text from the clipboard to the current edit widget.
### 2. `ModuleInspector`
-Kelas ini bertanggung jawab untuk memuat dan menginspeksi modul-modul Python. Informasi yang dapat diambil meliputi variabel global, kelas, dan fungsi dalam modul.
+This class is responsible for loading and inspecting Python modules. Retrievable information includes global variables, classes, and functions in the module.
-#### Atribut:
-- **modules**: Menyimpan daftar nama modul yang ditemukan di `sys.path`.
+#### Attributes:
+- **modules**: Stores a list of module names found in `sys.path`.
-#### Metode:
-- **`get_module(self, paths)`**: Mengembalikan daftar modul yang ditemukan di path yang diberikan.
-- **`inspect_module(self, module_name)`**: Menginspeksi modul dengan nama yang diberikan dan mengembalikan detail modul tersebut.
+#### Method:
+- **`get_module(self, paths)`**: Returns a list of modules found in the given path.
+- **`inspect_module(self, module_name)`**: Inspects the module with the given name and returns the details of the module.
## Penggunaan
1. **Menjalankan Aplikasi**: Jalankan script `SuperNano` dengan Python 3.6 ke atas di terminal Anda.
@@ -65,22 +65,23 @@ Kelas ini bertanggung jawab untuk memuat dan menginspeksi modul-modul Python. In
6. **Inspeksi Modul**: Pilih modul dari daftar yang tersedia di UI untuk menampilkan informasi tentang modul tersebut.
7. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi.
-## Cara Penggunaan
-Jalankan script ini melalui command line dengan memberikan argumen berupa path file atau direktori yang ingin diedit. Contoh:
+## How to run script
+Run this script through the command line by giving an argument in the form of the path of the file or directory you want to edit. Example:
```
python supernano.py /path/to/directory_or_file
```
+or [main](https://github.com/LcfherShell/SuperNano/tree/main)
-## Lisensi
-Aplikasi ini dibuat oleh Ramsyan Tungga Kiansantang dan dilisensikan di bawah [Lisensi GPL v3](https://www.gnu.org/licenses/gpl-3.0.html). Untuk kontribusi atau pelaporan bug, silakan kunjungi repositori Github yang telah disediakan.
+## License
+This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://www.gnu.org/licenses/gpl-3.0.html). For contributions or bug reporting, please visit the provided Github repository.
-## Versi
-- **Versi**: V2.1.0
-- **Tanggal Rilis**: 21 Agustus 2024
+## Version
+- **Version**: V2.1.0
+- **Release Date**: August 21, 2024
---
-## Kesimpulan
-`SuperNano` adalah editor teks berbasis konsol yang dirancang untuk memudahkan pengelolaan file dan direktori secara langsung dari command line. Aplikasi ini menawarkan alat yang kuat untuk pengguna yang bekerja di lingkungan berbasis teks.
+## Conclusion
+`SuperNano` is a console-based text editor designed to make it easy to manage files and directories directly from the command line. It offers powerful tools for users working in a text-based environment.
-Jika ada pertanyaan atau butuh bantuan lebih lanjut terkait implementasi, jangan ragu untuk menghubungi pengembang atau melihat dokumentasi tambahan yang mungkin tersedia.
+If you have any questions or need further assistance with the implementation, feel free to contact the developer or check out any additional documentation that may be available. [Email Support](mailto:alfiandecker2@gmail.com,ramstungga2@gmail.com)
From 2b33028062a6b58d0cb2bb42bee65559cb97e7c8 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 22:37:40 +0700
Subject: [PATCH 11/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/README.md b/README.md
index 258e43e..949dc3d 100644
--- a/README.md
+++ b/README.md
@@ -56,14 +56,14 @@ This class is responsible for loading and inspecting Python modules. Retrievable
- **`get_module(self, paths)`**: Returns a list of modules found in the given path.
- **`inspect_module(self, module_name)`**: Inspects the module with the given name and returns the details of the module.
-## Penggunaan
-1. **Menjalankan Aplikasi**: Jalankan script `SuperNano` dengan Python 3.6 ke atas di terminal Anda.
-2. **Navigasi File**: Gunakan panah atas dan bawah untuk memilih file di direktori. Tekan Enter untuk membuka file.
-3. **Edit Teks**: Setelah file terbuka, teks dapat diedit langsung di editor. Gunakan `Ctrl+S` untuk menyimpan perubahan.
-4. **Undo-Redo**: Gunakan `Ctrl+Z` untuk undo dan `Ctrl+Y` untuk redo.
-5. **Copy-Paste**: Gunakan `Ctrl+C` untuk copy dan `Ctrl+V` untuk paste.
-6. **Inspeksi Modul**: Pilih modul dari daftar yang tersedia di UI untuk menampilkan informasi tentang modul tersebut.
-7. **Keluar dari Aplikasi**: Tekan `Ctrl+Q` atau `ESC` untuk keluar dari aplikasi.
+## Usage
+1. **Running the Application**: Run the `SuperNano` script with Python 3.6 and above in your terminal.
+2. **Navigate Files**: Use the up and down arrows to select files in the directory. Press Enter to open the file.
+3. **Edit Text**: Once the file is open, the text can be edited directly in the editor. Use `Ctrl+S` to save changes.
+4. **Undo-Redo**: Use `Ctrl+Z` to undo and `Ctrl+Y` to redo.
+5. **Copy-Paste**: Use `Ctrl+C` to copy and `Ctrl+V` to paste.
+6. **Inspect Module**: Select a module from the list available in the UI to display information about the module.
+7. **Exit Application**: Press `Ctrl+Q` or `ESC` to exit the application.
## How to run script
Run this script through the command line by giving an argument in the form of the path of the file or directory you want to edit. Example:
From 0edd4dfc7521572091c77374da8c7552cc44d48b Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 22:52:27 +0700
Subject: [PATCH 12/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 949dc3d..8501897 100644
--- a/README.md
+++ b/README.md
@@ -46,7 +46,7 @@ Here is the documentation for the `SuperNano` script, a powerful console-based t
- **`copy_text_to_clipboard(self)`**: Copies the text from the current edit widget to the clipboard.
- **`paste_text_from_clipboard(self)`**: Paste text from the clipboard to the current edit widget.
-### 2. `ModuleInspector`
+### `ModuleInspector`
This class is responsible for loading and inspecting Python modules. Retrievable information includes global variables, classes, and functions in the module.
#### Attributes:
From 04038c47ed0923706836d9e1f80e3621e153b2c2 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 23:04:01 +0700
Subject: [PATCH 13/40] Add files via upload
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
GPL-3.0.txt | 154 ++++++++++++++++++++++++++++++++++++++++++++++++
MIT-LICENSE.txt | 21 +++++++
2 files changed, 175 insertions(+)
create mode 100644 GPL-3.0.txt
create mode 100644 MIT-LICENSE.txt
diff --git a/GPL-3.0.txt b/GPL-3.0.txt
new file mode 100644
index 0000000..b9c1343
--- /dev/null
+++ b/GPL-3.0.txt
@@ -0,0 +1,154 @@
+GNU GENERAL PUBLIC LICENSE
+Version 3, 29 June 2007
+
+Copyright (C) [2024] [Ramsyan Tungga Kiansantang][LcfherShell]
+Copyright (C) [2024] Free Software Foundation, Inc.
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+===
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+Copyright (C) 2007 Free Software Foundation, Inc.
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The GNU General Public License is a free, copyleft license for software
+and other kinds of works. The licenses for most software and other
+practical works are designed to take away your freedom to share and
+change the works. By contrast, our General Public Licenses are intended
+to guarantee your freedom to share and change all versions of a program
+to make sure it remains free software for all its users.
+
+When we speak of free software, we are referring to freedom, not price.
+Our General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this service
+if you wish), that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free programs,
+and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone
+to deny you these rights or to ask you to surrender the rights. These
+restrictions translate to certain responsibilities for you if you distribute
+copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis
+or for a fee, you must pass on the same freedoms to the recipients that you
+received. You must make sure that they, too, receive or can get the source
+code. And you must show them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If
+the software is modified by someone else and passed on, we want its
+recipients to know that what they have is not the original, so that any
+problems introduced by others will not reflect on the original authors'
+reputations.
+
+Finally, any free program is threatened constantly by software patents.
+We wish to avoid the danger that redistributors of a free program will
+individually obtain patent licenses, in effect making the program
+proprietary. To prevent this, we have made it clear that any patent must
+be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+0. Definitions.
+
+"This License" refers to version 3 of the GNU General Public License.
+
+"Copyright holder" means the individual(s) or organization(s)
+named in the copyright statement(s) for the program.
+
+"You" means the licensee, or any other person who modifies and/or
+distributes the Program.
+
+"Program" means the work licensed under this License.
+
+"Modified version" means the Program with changes made to it.
+
+"Source code" means the preferred form of the Program for making
+modifications to it.
+
+"Object code" means any non-source form of a work.
+
+"Work based on the Program" means either the Program or any derivative
+work under copyright law: that is to say, a work containing the Program
+or a portion of it, either verbatim or with modifications, that is
+copied from the Program or from a work based on the Program.
+
+"Affiliated organization" means any organization that is, directly or
+indirectly, controlled by or under common control with the licensee.
+
+1. Source Code.
+
+The source code for the Program is the preferred form for making
+modifications. The source code must be distributed as part of the Program.
+
+2. Copyleft.
+
+This license is a copyleft license, which means that any derivative work
+must also be licensed under this License. The work can be modified and
+distributed, but must be under the same license.
+
+3. Distribution of Modified Code.
+
+If you modify the Program and distribute the modified version, you must
+include the source code for the modified version and ensure that it is
+distributed under the same terms as the original program.
+
+4. License.
+
+This License is designed to ensure that the Program remains free. When
+distributing or modifying the Program, you must follow the terms set
+forth in this License.
+
+5. No Warranty.
+
+There is no warranty for the Program. It is provided "as is" without
+any express or implied warranties.
+
+6. Termination.
+
+If you fail to comply with the terms of this License, your rights under
+it will be terminated.
+
+7. Additional Terms.
+
+You may not impose additional restrictions on the rights granted by this
+License.
+
+8. Acceptance.
+
+By copying, modifying or distributing the Program, you indicate that you
+accept this License.
+
+9. Miscellaneous.
+
+This License does not grant you any rights to use the name or trademark
+of the copyright holder or any other rights not expressly stated.
+
+END OF TERMS AND CONDITIONS
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt
new file mode 100644
index 0000000..d41968e
--- /dev/null
+++ b/MIT-LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) [2024] [Ramsyan Tungga Kiansantang] [LcfherShell]
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
From 157209f4650cebb69c3bfa42e0846b09ac28f52d Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 23:20:39 +0700
Subject: [PATCH 14/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8501897..8df42fa 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ python supernano.py /path/to/directory_or_file
or [main](https://github.com/LcfherShell/SuperNano/tree/main)
## License
-This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://www.gnu.org/licenses/gpl-3.0.html). For contributions or bug reporting, please visit the provided Github repository.
+This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://github.com/LcfherShell/SuperNano/blob/V2.1.0/GPL-3.0.txt. For contributions or bug reporting, please visit the provided Github repository.
## Version
- **Version**: V2.1.0
From 158a0158755f023620b3b8ac6cda209e4e190f4b Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 23:21:08 +0700
Subject: [PATCH 15/40] README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8df42fa..b10ddc4 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,7 @@ python supernano.py /path/to/directory_or_file
or [main](https://github.com/LcfherShell/SuperNano/tree/main)
## License
-This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://github.com/LcfherShell/SuperNano/blob/V2.1.0/GPL-3.0.txt. For contributions or bug reporting, please visit the provided Github repository.
+This application was created by Ramsyan Tungga Kiansantang and is licensed under the [GPL v3 License](https://github.com/LcfherShell/SuperNano/blob/V2.1.0/GPL-3.0.txt). For contributions or bug reporting, please visit the provided Github repository.
## Version
- **Version**: V2.1.0
From 7118532707306c24064f80edddef35fdb11fb519 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 1 Sep 2024 23:21:52 +0700
Subject: [PATCH 16/40] Delete MIT-LICENSE.txt
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
MIT-LICENSE.txt | 21 ---------------------
1 file changed, 21 deletions(-)
delete mode 100644 MIT-LICENSE.txt
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt
deleted file mode 100644
index d41968e..0000000
--- a/MIT-LICENSE.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) [2024] [Ramsyan Tungga Kiansantang] [LcfherShell]
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
From 5264749c741fbd7d1596c63b34ef5a2055be7f14 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 01:21:12 +0700
Subject: [PATCH 17/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/supernano.py b/supernano.py
index 273b325..7de0d2f 100644
--- a/supernano.py
+++ b/supernano.py
@@ -934,9 +934,6 @@ def main(path: str):
app = SuperNano(start_path=path)
app.run()
-if '__file__' in globals():
- print("This script should not be imported. Run it directly.")
- sys.exit()
if __name__ == "__main__":
set_low_priority(os.getpid())
@@ -959,3 +956,7 @@ def main(path: str):
print(r)
rd.eraseFile() # membersihkan loggging
rd.close()
+else:
+ if '__file__' in globals():
+ print("This script should not be imported. Run it directly.")
+ sys.exit()
From fe2582d0a2cff966f8fc7f000d5398562f16e913 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 01:26:33 +0700
Subject: [PATCH 18/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/supernano.py b/supernano.py
index 7de0d2f..992ea2b 100644
--- a/supernano.py
+++ b/supernano.py
@@ -956,7 +956,3 @@ def main(path: str):
print(r)
rd.eraseFile() # membersihkan loggging
rd.close()
-else:
- if '__file__' in globals():
- print("This script should not be imported. Run it directly.")
- sys.exit()
From 65e65fd04498119ff5abad54860bce36e5b92b52 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 01:50:42 +0700
Subject: [PATCH 19/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/supernano.py b/supernano.py
index 992ea2b..953382f 100644
--- a/supernano.py
+++ b/supernano.py
@@ -520,18 +520,26 @@ def close_popup(self, button):
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def get_file_list(self):
"Mengambil daftar file dan direktori di path saat ini, termasuk opsi untuk naik satu level di direktori jika bukan di direktori root."
+
files = []
+
if self.current_path != ".": # Cek apakah bukan di direktori root
button = PlainButton("...")
+
urwid.connect_signal(button, "click", self.go_up_directory)
+
files.append(urwid.AttrMap(button, None, focus_map="reversed"))
- for f in os.listdir(self.current_path):
- if os.path.isdir(os.path.join(self.current_path, f)):
+ for f in os.listdir(f"{self.current_path}"):
+ if os.path.isdir(resolve_relative_path(self.current_path, f)):
f = f + "/"
+
button = PlainButton(f)
+
urwid.connect_signal(button, "click", self.open_file, f)
+
files.append(urwid.AttrMap(button, None, focus_map="reversed"))
+
return files
def handle_input(self, key):
From 9002e1f393cb59edd2b8c714a4580493c30e2486 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:15:14 +0700
Subject: [PATCH 20/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/supernano.py b/supernano.py
index 953382f..ef39a90 100644
--- a/supernano.py
+++ b/supernano.py
@@ -116,15 +116,9 @@ def parse_args():
parser = argparse.ArgumentParser(
description="An extension on nano for editing directories in CLI."
)
- parser.add_argument(
- "path",
- default=os.path.split(thisfolder)[0],
- nargs="?",
- type=str,
- help="Target file or directory to edit.",
- )
- args = parser.parse_args()
- path = resolve_relative_path(args.path, "") or "."
+ parser.add_argument("path", help="Target file or directory to edit.")
+ args = vars(parser.parse_args())
+ path = args.get("path", ".").strip().replace("\\", "/")
if os.path.exists(path):
if validate_folder(path=path):
pass
From a099bcf6636ab5670c512ee889423122a0796b38 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:23:04 +0700
Subject: [PATCH 21/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/supernano.py b/supernano.py
index ef39a90..ad68349 100644
--- a/supernano.py
+++ b/supernano.py
@@ -15,6 +15,7 @@
validate_file,
ModuleInspector,
create_file_or_folder,
+ resolve_relative_path,
resolve_relative_path_v2,
all_system_paths,
)
@@ -31,6 +32,7 @@
validate_file,
ModuleInspector,
create_file_or_folder,
+ resolve_relative_path,
resolve_relative_path_v2,
all_system_paths,
)
@@ -46,6 +48,7 @@
validate_file,
ModuleInspector,
create_file_or_folder,
+ resolve_relative_path,
resolve_relative_path_v2,
all_system_paths,
)
@@ -111,22 +114,39 @@ def setTitle(title: str):
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def parse_args():
"""
+
Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n
+
"""
+
parser = argparse.ArgumentParser(
description="An extension on nano for editing directories in CLI."
)
- parser.add_argument("path", help="Target file or directory to edit.")
- args = vars(parser.parse_args())
- path = args.get("path", ".").strip().replace("\\", "/")
+
+ parser.add_argument(
+ "path",
+ default=os.path.split(thisfolder)[0],
+ nargs="?",
+ type=str,
+ help="Target file or directory to edit.",
+ )
+
+ args = parser.parse_args()
+
+ path = resolve_relative_path(args.path, "") or "."
+
if os.path.exists(path):
if validate_folder(path=path):
pass
+
else:
logging.error(f"ERROR - {path} path cannot access")
+
exit()
+
else:
logging.error(f"ERROR - {path} path does not exist")
+
exit()
return resolve_relative_path_v2(path).replace("\\", "/")
From 939d829d65ae5dc851b2743bf90c698c9912e5f2 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:27:35 +0700
Subject: [PATCH 22/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 19d39c0..0676d54 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -238,7 +238,7 @@ def inspect_module(self, module_name):
global_vars = self.get_global_variables(module_name)
result = {
"module": module_name,
- "global_variables": global_vars,
+ "variables": global_vars,
"classes": [],
}
From bebf2de8bf8633ab0c65727c2367d6d88e7e1e4e Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:32:10 +0700
Subject: [PATCH 23/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 0676d54..3c2117f 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -204,6 +204,17 @@ def get_class_details(self, cls):
details["variables"].append(name)
return details
+
+ def get_function_detail(self, module):
+ details = []
+ try:
+ for name, obj in inspect.getmembers(importlib.import_module(module)):
+ if inspect.isfunction(obj):
+ func_details = {"name": name, "params": str(inspect.signature(obj))}
+ details.append(func_details)
+ except:
+ pass
+ return details
def get_global_variables(self, module):
try:
@@ -240,6 +251,7 @@ def inspect_module(self, module_name):
"module": module_name,
"variables": global_vars,
"classes": [],
+ "functions": self.get_function_detail(module_name),
}
for cls in classes:
From f223d68a6f3e364b1cb28434a22d91ca4e01845f Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:35:02 +0700
Subject: [PATCH 24/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 1298 ++++++++++++++++++++++++++++++-------------
1 file changed, 926 insertions(+), 372 deletions(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 3c2117f..ad68349 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -1,426 +1,980 @@
-import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading
+import urwid
+import pyperclip
+import os, sys, shutil, logging, time, threading, argparse
+from datetime import datetime
try:
- from .helperegex import (
- searchmissing,
- searching,
- fullmacth,
- rremovelist,
- clean_string,
- rreplace,
- cleanstring,
+ from libs.helperegex import findpositions
+ from libs.titlecommand import get_console_title, set_console_title
+ from libs.cmd_filter import shorten_path, validate_folder
+ from libs.errrorHandler import complex_handle_errors
+ from libs.system_manajemen import set_low_priority, SafeProcessExecutor
+ from libs.timeout import timeout_v2, timeout_v1
+ from libs.filemanager import (
+ StreamFile,
+ validate_file,
+ ModuleInspector,
+ create_file_or_folder,
+ resolve_relative_path,
+ resolve_relative_path_v2,
+ all_system_paths,
)
- from .cmd_filter import filter_json, safe_load_json
- from .system_manajemen import set_low_priority, SafeProcessExecutor
- from .timeout import timeout_v1, timeout_v2
- from .https import Fetch
except:
try:
- from helperegex import (
- searchmissing,
- searching,
- fullmacth,
- rremovelist,
- clean_string,
- rreplace,
- cleanstring,
+ from .helperegex import findpositions
+ from .titlecommand import get_console_title, set_console_title
+ from .cmd_filter import shorten_path, validate_folder
+ from .errrorHandler import complex_handle_errors
+ from .system_manajemen import set_low_priority, SafeProcessExecutor
+ from .timeout import timeout_v2, timeout_v1
+ from .filemanager import (
+ StreamFile,
+ validate_file,
+ ModuleInspector,
+ create_file_or_folder,
+ resolve_relative_path,
+ resolve_relative_path_v2,
+ all_system_paths,
)
- from cmd_filter import filter_json, safe_load_json
- from system_manajemen import set_low_priority, SafeProcessExecutor
- from timeout import timeout_v1, timeout_v2
- from https import Fetch
except:
- from libs.helperegex import (
- searchmissing,
- searching,
- fullmacth,
- rremovelist,
- clean_string,
- rreplace,
- cleanstring,
+ from helperegex import findpositions
+ from titlecommand import get_console_title, set_console_title
+ from cmd_filter import shorten_path, validate_folder
+ from errrorHandler import complex_handle_errors
+ from system_manajemen import set_low_priority, SafeProcessExecutor
+ from timeout import timeout_v2, timeout_v1
+ from filemanager import (
+ StreamFile,
+ validate_file,
+ ModuleInspector,
+ create_file_or_folder,
+ resolve_relative_path,
+ resolve_relative_path_v2,
+ all_system_paths,
)
- from libs.cmd_filter import filter_json, safe_load_json
- from libs.system_manajemen import set_low_priority, SafeProcessExecutor
- from libs.timeout import timeout_v1, timeout_v2
- from libs.https import Fetch
-if __name__ == "__main__":
- set_low_priority(os.getpid())
+set_low_priority(os.getpid())
+#########mendapatkan process terbaik tanpa membebani ram dan cpu
+thisfolder, _x = all_system_paths
+__version__ = "2.1.0"
-script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")
-all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir]
+fileloogiing = os.path.join(thisfolder, "cache", "file_browser.log").replace("\\", "/")
+if not os.path.isfile(fileloogiing):
+ open(fileloogiing, "a+")
+elif os.path.getsize(fileloogiing) > 0:
+ with open(fileloogiing, "wb+") as f:
+ f.truncate(0)
-class StreamFile:
- def __init__(self, file_path: str, buffer_size: int = 8192, print_delay: float = 2):
- """
- Inisialisasi StreamFile untuk membaca file baris demi baris dengan delay dan menulis dengan buffer.
+for handler in logging.root.handlers[:]:
+ logging.root.removeHandler(handler)
- :param file_path: Path ke file yang akan dibaca atau ditulis.
- :param buffer_size: Ukuran buffer sebelum data ditulis ke file.
- :param print_delay: Waktu jeda (dalam detik) antara print setiap baris.
- """
- self.file_path = file_path
- self.buffer_size = buffer_size or 0
- self.print_delay = print_delay
- self.buffer = bytearray()
+logging.basicConfig(
+ filename=fileloogiing,
+ filemode="w",
+ encoding=sys.getfilesystemencoding(),
+ format="%(asctime)s, %(msecs)d %(name)s %(levelname)s [ %(filename)s-%(module)s-%(lineno)d ] : %(message)s",
+ datefmt="%H:%M:%S",
+ level=logging.ERROR,
+)
+logging.getLogger("urwid").disabled = True
+logger = logging.getLogger("urwid")
+for handler in logger.handlers[:]:
+ logger.removeHandler(handler)
- def readlines(self):
- """
- Membaca file dengan buffer size dan menghasilkan setiap baris satu per satu dengan delay.
- :yield: Baris dari file.
- """
+def setTitle(title: str):
+ """
+ Fungsi setTitle bertugas untuk mengatur judul konsol (console title) berdasarkan parameter title yang diberikan.\n
+ Jika inputan title memiliki panjang lebih dari 30 karakter maka potong karakternya
+ """
+ process = title
+ Getitles = get_console_title()
+ if os.path.isdir(process) or os.path.isfile(process):
+ length = int(process.__len__() / 2)
+ if length < 28:
+ x = process.__len__()
+ nexts = int(50 - x) - (x / 2)
+ if nexts < 28:
+ length = int((28 - nexts) + nexts)
+ else:
+ length = nexts
+ elif length > 50:
+ length = 28
+ process = shorten_path(process, length)
+
+ if Getitles.startswith("Win-SuperNano"):
+ output = str("Win-SuperNano {titles}".format(titles=process))
+ else:
+ output = title
+ set_console_title(output)
- with open(self.file_path, "r+") as f:
- buffer = self.buffer
- while True:
- chunk = f.read(self.buffer_size)
- if not chunk:
- break
- buffer.extend(
- chunk.encode("utf-8")
- ) # Encode chunk to bytes if necessary
+@complex_handle_errors(loggering=logging, nomessagesNormal=False)
+def parse_args():
+ """
- while b"\n" in buffer:
- line, buffer = buffer.split(b"\n", 1)
- yield line.decode("utf-8")
- time.sleep(self.print_delay)
+ Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n
- if buffer:
- yield buffer.decode("utf-8")
- self.buffer_size = 0
+ """
- def write(self, data):
- """
- Menulis data ke buffer dan secara otomatis menulis ke file ketika buffer penuh.
+ parser = argparse.ArgumentParser(
+ description="An extension on nano for editing directories in CLI."
+ )
- :param data: Data yang akan ditulis ke buffer.
- """
- self.buffer.extend(data)
- while len(self.buffer) >= self.buffer_size:
- with open(self.file_path, "ab+") as f:
- f.write(self.buffer[: self.buffer_size])
- self.buffer = self.buffer[self.buffer_size :]
+ parser.add_argument(
+ "path",
+ default=os.path.split(thisfolder)[0],
+ nargs="?",
+ type=str,
+ help="Target file or directory to edit.",
+ )
- def writelines(self, lines):
- """
- Menulis baris-baris data ke file dengan delay antara setiap baris.
+ args = parser.parse_args()
- :param lines: List atau generator yang menghasilkan baris-baris data untuk ditulis.
- """
- for line in lines:
- self.write(line.encode("utf-8"))
- time.sleep(self.print_delay + timeout_v1())
- self.close() # Memastikan buffer ditulis dan ditutup setelah penulisan selesai
+ path = resolve_relative_path(args.path, "") or "."
- def eraseFile(self):
- with open(self.file_path, "rb+") as f:
- f.truncate(0)
+ if os.path.exists(path):
+ if validate_folder(path=path):
+ pass
- def close(self):
- """
- Menulis sisa data di buffer ke file dan membersihkan buffer.
- """
- if self.buffer and self.buffer_size:
- with open(self.file_path, "ab+") as f:
- f.write(self.buffer)
- self.buffer.clear()
else:
- pass
+ logging.error(f"ERROR - {path} path cannot access")
+ exit()
-class ModuleInspector:
- def __init__(self):
- self.modules = self.getsys_module()
- self.curents = self.modules
- self.curentpath = sys.path.copy()
- self.modulepathnow = []
+ else:
+ logging.error(f"ERROR - {path} path does not exist")
- def getsys_module(self):
- return sorted(
- [
- module.name
- for module in pkgutil.iter_modules([x for x in sys.path if x])
- if not module.name.strip().startswith("~")
- and not module.name.strip().startswith("__pycache__")
- ]
- )
-
- def get_module(self, paths:list=[]):
- def getmodules(path:list, result:list):
- result.extend(sorted(
- [
- module.name
- for module in pkgutil.iter_modules(path)
- if not module.name.strip().startswith("~")
- and not module.name.strip().startswith("__pycache__")
- ]
- ))
+ exit()
+
+ return resolve_relative_path_v2(path).replace("\\", "/")
- threads, result = [[], self.curents]
- if paths.__len__()<1:
- paths = [os.getcwd()]
+
+class PlainButton(urwid.Button):
+ """
+ Class PlainButton bertugas untuk mengkoustomisasi button dan menghilangkan karakter < dan >.\n
+ """
+
+ button_left = urwid.Text("")
+ button_right = urwid.Text("")
+
+
+class EditableButton(urwid.WidgetWrap):
+ def __init__(self, label, on_save):
+ self.label = label
+ self.on_save = on_save
+ self.button = PlainButton(label)
+ self.last_click_time = 0
+ urwid.connect_signal(self.button, "click", self.on_click)
+ self._w = self.button
+
+ def on_click(self, button):
+ current_time = time.time()
+ # Check for double click within 0.3 seconds
+ if current_time - self.last_click_time < 0.3:
+ self.edit_text(button)
+ self.last_click_time = current_time
+
+ def edit_text(self, button):
+ self.edit = urwid.Edit(multiline=False, edit_text=self.label)
+ urwid.connect_signal(self.edit, "change", self.on_change)
+ self._w = urwid.AttrMap(self.edit, None, focus_map="reversed")
+
+ def on_change(self, edit, new_text):
+ self.label = new_text
+
+ def keypress(self, size, key):
+ if isinstance(self._w.base_widget, urwid.Edit):
+ if key == "enter":
+ self.save_and_restore()
+ else:
+ return self._w.keypress(size, key)
else:
- pass
-
-
- for path in paths:
- thread = threading.Thread(target=getmodules, args=([path], result))
- thread.start()
- threads.append(thread)
-
- for thread in threads:
- thread.join()
-
- self.modulepathnow = paths
- return result
-
-
- def list_classes(self, module):
- try:
- imported_module = importlib.import_module(module)
- classes = [
- obj
- for name, obj in inspect.getmembers(imported_module)
- if inspect.isclass(obj)
- ]
- if not classes:
- pass
- return classes
- except Exception as e:
- return []
-
- def get_class_details(self, cls):
- details = {"name": cls.__name__, "variables": [], "functions": []}
-
- for name, obj in inspect.getmembers(cls):
- if inspect.isfunction(obj):
- func_details = {"name": name, "params": str(inspect.signature(obj))}
- details["functions"].append(func_details)
- elif not name.startswith("__") and not inspect.ismodule(obj):
- details["variables"].append(name)
-
- return details
-
- def get_function_detail(self, module):
- details = []
- try:
- for name, obj in inspect.getmembers(importlib.import_module(module)):
- if inspect.isfunction(obj):
- func_details = {"name": name, "params": str(inspect.signature(obj))}
- details.append(func_details)
- except:
- pass
- return details
-
- def get_global_variables(self, module):
- try:
- imported_module = importlib.import_module(module)
- # global_vars = {name: self.serialize_value(value) for name, value in vars(imported_module).items()
- # if not (inspect.isclass(value) or inspect.isfunction(value)) and not name.startswith('__')}
- global_vars = [
- name
- for name, value in vars(imported_module).items()
- if not (inspect.isclass(value) or inspect.isfunction(value))
- and not name.startswith("__")
- ]
- return global_vars
- except Exception as e:
- return []
-
- def serialize_value(self, value):
- """Serialize values for JSON compatibility."""
- if isinstance(value, (int, float, str, bool, list, dict)):
- return value
- elif callable(value):
- return f"Function: {value.__name__}"
+ return super().keypress(size, key)
+
+ def save_and_restore(self):
+ self.on_save(self.label)
+ self.button = PlainButton(self.label)
+ urwid.connect_signal(self.button, "click", self.on_click)
+ self._w = self.button
+
+
+class ClipboardTextBox(urwid.Edit):
+ def keypress(self, size, key):
+ if key == "ctrl c":
+ self.copy_to_clipboard()
+ elif key == "ctrl v":
+ self.paste_from_clipboard()
else:
- return str(value) # Convert other types to string
-
- def inspect_module(self, module_name):
- if self.modulepathnow.__len__()>=1:
- sys.path.extend(self.modulepathnow)
- self.modulepathnow = []
- try:
- classes = self.list_classes(module_name)
- global_vars = self.get_global_variables(module_name)
- result = {
- "module": module_name,
- "variables": global_vars,
- "classes": [],
- "functions": self.get_function_detail(module_name),
- }
-
- for cls in classes:
- class_details = self.get_class_details(cls)
- result["classes"].append(class_details)
-
- # Convert the result to JSON and print it
-
- sys.path = self.curentpath
- return result
- except Exception as e:
- sys.path = self.curentpath
- return None
-
-
-def create_file_or_folder(path: str) -> str:
+ return super().keypress(size, key)
+
+ def copy_to_clipboard(self):
+ self.clipboard = self.get_edit_text()
+
+ def paste_from_clipboard(self):
+ if hasattr(self, "clipboard"):
+ cursor_pos = self.edit_pos
+ text = self.get_edit_text()
+ self.set_edit_text(text[:cursor_pos] + self.clipboard + text[cursor_pos:])
+ self.edit_pos = cursor_pos + len(self.clipboard)
+
+
+class SuperNano:
"""
- Membuat file atau folder di path yang diberikan.
+ Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses].
- Args:
- path (str): Path lengkap tempat file atau folder akan dibuat.
+ Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell
- Returns:
- str: Pesan konfirmasi yang menunjukkan apakah file atau folder berhasil dibuat.
+ Tanggal dibuat: 21 Agustus 2024
+
+ Jika ada bug silahkan kunjungi git yang telah tertera diatas
"""
- if not path:
- return "Path is empty."
-
- if os.path.isdir(path):
- return f"The folder '{os.path.basename(path)}' already exists."
-
- if os.path.isfile(path):
- return f"The file '{os.path.basename(path)}' already exists."
-
- folder, filename = os.path.split(path)
- if "." in os.path.basename(path) and os.path.exists(folder):
- # Membuat file
- try:
- if folder and not os.path.exists(folder):
- return f"Failed to create the file '{filename}'"
- with open(path, "wb") as f:
- pass # Membuat file kosong
- return f"The file '{filename}' has been successfully created."
- except Exception as e:
- return f"Failed to create the file '{filename}'"
- elif os.path.exists(folder) and folder:
- # Membuat folder
- try:
- os.makedirs(path)
- return (
- f"The folder '{os.path.basename(path)}' has been successfully created."
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def __init__(self, start_path="."):
+ "Mengatur path awal, judul aplikasi, widget, dan layout utama. Juga mengatur alarm untuk memuat menu utama dan memulai loop aplikasi."
+ self.current_path = start_path
+ self.current_pathx = self.current_path
+
+ self.current_file_name = None # Track current file name
+ self.undo_stack, self.redo_stack = [[], []] # Stack for undo # Stack for redo
+ self.overlay_POPUP = None # Overlay untuk popup
+ self.module_package_Python = ModuleInspector() # memuat module python
+
+ # Set title
+ setTitle("Win-SuperNano v{version}".format(version=__version__))
+
+ # Create widgets
+ """
+ 1.loading menu
+ 2. main menu: search, list file or folder, and inspect module python
+ """
+
+ ######Create widgets modulepython menu
+ def create_button(module_name):
+ button = PlainButton(module_name)
+ urwid.connect_signal(button, "click", self.inspect_module, module_name)
+ return urwid.AttrMap(button, None, focus_map="reversed")
+
+ self.listmodules_from_package_Python = urwid.SimpleFocusListWalker(
+ [
+ create_button(module)
+ for module in self.module_package_Python.get_module(sys.path)
+ ]
+ )
+ # Footer text and ListBox for scrolling
+ self.Text_Deinspect_modules_from_package_Python = urwid.Text(
+ "Select a module to inspect."
+ )
+ MenuText_Inspect_modules_from_package_Python = urwid.ListBox(
+ urwid.SimpleFocusListWalker(
+ [self.Text_Deinspect_modules_from_package_Python]
)
- except FileExistsError:
- return f"The folder '{os.path.basename(path)}' already exists."
- except Exception as e:
- return f"Failed to create the folder '{os.path.basename(path)}'."
- else:
- return "Something happened."
+ )
+ Box_Deinspect_modules_from_package_Python = urwid.BoxAdapter(
+ MenuText_Inspect_modules_from_package_Python, 14
+ ) # Set max height for the footer
+ # Use a Frame to wrap the main content and footer
+ self.Inspect_modules_from_package_Python = urwid.Frame(
+ body=urwid.LineBox(
+ urwid.ListBox(self.listmodules_from_package_Python),
+ title="Python Modules",
+ ),
+ footer=Box_Deinspect_modules_from_package_Python,
+ )
+ ###Create widgets loading menu
+ self.title_loading_widget = urwid.Text(
+ "Win-SuperNano v{version} CopyRight: LcfherShell@{year}\n".format(
+ version=__version__, year=datetime.now().year
+ ),
+ align="center",
+ )
+ self.loading_widget = urwid.Text("Loading, please wait...", align="center")
+ self.main_layout = urwid.Filler(
+ urwid.Pile([self.title_loading_widget, self.loading_widget]),
+ valign="middle",
+ )
-def is_binary_file(file_path):
- """
- Menentukan apakah file adalah file biner atau bukan.
+ # Create main menu
+ self.main_menu_columns = urwid.Columns([])
+ self.main_menu_pile = urwid.Pile([self.main_menu_columns])
+ self.status_msg_footer_text = urwid.Text(
+ "Press ctrl + q to exit, Arrow keys to navigate"
+ )
+ self.main_footer_text = urwid.Text(
+ "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+R : Refresh UI ESC: Quit "
+ )
- Args:
- file_path (str): Path ke file yang akan diperiksa.
+ # Event loop
+ self.loop = urwid.MainLoop(self.main_layout, unhandled_input=self.handle_input)
+ self.loading_alarm = self.loop.set_alarm_in(
+ round(timeout_v1() * timeout_v2(), 1) + 1,
+ lambda loop, user_data: self.load_main_menu(),
+ )
+ self.system_alarm = None
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def load_main_menu(self):
+ "Menyiapkan dan menampilkan menu utama setelah periode loading, dan menghapus alarm loading."
+ # self.loading_widget.set_text("Press key R")
+ set_low_priority(os.getpid())
+ self.loop.remove_alarm(self.loading_alarm) # Hentikan alarm
+ self.loading_alarm = None
+ self.switch_to_secondary_layout()
+
+ def switch_to_secondary_layout(self):
+ "Mengubah layout aplikasi ke menu utama yang telah disiapkan."
+ self.setup_main_menu()
+ if self.loading_alarm != None:
+ self.loop.remove_alarm(
+ self.loading_alarm
+ ) # Hentikan alarm loading jika masih ada
+ self.loading_alarm = None
+ self.loop.widget = self.main_layout
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def setup_main_menu(self):
+ "Menyiapkan dan mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. Mengatur layout untuk tampilan aplikasi."
+ # Define widgets
+ self.file_list = urwid.SimpleFocusListWalker(self.get_file_list())
+ self.file_list_box = urwid.ListBox(self.file_list)
+ self.text_editor = urwid.Edit(multiline=True)
+ self.current_focus = 0 # 0 for textbox1, 1 for textbox2
+ # Wrap text_editor with BoxAdapter for scrollable content
+ self.text_editor_scrollable = urwid.LineBox(
+ urwid.Filler(self.text_editor, valign="top")
+ )
- Returns:
- bool: True jika file adalah file biner, False jika bukan.
- """
- try:
- with open(file_path, "rb") as file:
- chunk = file.read(1024) # Membaca bagian pertama file (1KB)
- # Cek apakah file memiliki karakter yang tidak biasa untuk teks
- if b"\0" in chunk: # Null byte adalah indikator umum dari file biner
- return True
- # Cek apakah file sebagian besar berisi karakter teks (misalnya ASCII)
- text_chars = b"".join([bytes((i,)) for i in range(32, 127)]) + b"\n\r\t\b"
- non_text_chars = chunk.translate(None, text_chars)
- if (
- len(non_text_chars) / len(chunk) > 0.30
- ): # Jika lebih dari 30% karakter non-teks
- return True
- return False
- except Exception as e:
- return False
-
-def validate_file(file_path, max_size_mb=100, max_read_time=2):
- try:
- # Periksa ukuran file
- file_size = os.path.getsize(file_path)
- if file_size > max_size_mb * 1024 * 1024:
- return False
-
- # Mulai waktu pembacaan
- start_time = time.time()
-
- # Baca file
- with open(file_path, "rb") as f:
- # Baca bagian pertama file untuk memeriksa apakah file biner
- first_bytes = f.read(1024)
- if b"\x00" in first_bytes:
- return False
-
- # Lanjutkan membaca file
- while f.read(1024):
- # Periksa waktu yang telah digunakan untuk membaca
- elapsed_time = time.time() - start_time
- if elapsed_time > max_read_time:
- return False
-
- # Jika semua pemeriksaan lolos, file valid
- return True
+ # Define menu widgets
+ self.quit_button = PlainButton("Quit", align="center")
+ urwid.connect_signal(self.quit_button, "click", self.quit_app)
- except Exception as e:
- return False
+ self.search_edit = urwid.Edit(
+ "Search, Rename or Create: ", multiline=False, align="left"
+ )
+ search_limited = urwid.BoxAdapter(
+ urwid.Filler(self.search_edit, valign="top"), height=1
+ )
+ self.search_button = PlainButton("Execute", align="center")
+ urwid.connect_signal(self.search_button, "click", self.in_search_)
-def check_class_in_package(package_name, class_name):
- try:
- # Import the package
- package = importlib.import_module(package_name)
- # Cek apakah kelas ada di dalam modul
- if hasattr(package, class_name):
- cls = getattr(package, class_name)
- # Pastikan itu adalah kelas, bukan atribut atau fungsi
- if inspect.isclass(cls):
- return True, "ClassFound"
- return False, "ClassNotFound"
- except ModuleNotFoundError:
- return False, "ModuleNotFoundError"
-
-
-def resolve_relative_path(current_path: str, relative_path: str) -> str:
- # Menggabungkan current_path dengan relative_path (misalnya "../")
- target_path: str = os.path.normpath(os.path.join(current_path, relative_path))
- return target_path
-
-
-def resolve_relative_path_v2(path: str) -> str:
- target_folder: str = resolve_relative_path(
- os.getcwd().replace("\\", "/"), path.replace("\\", "/")
- )
- return target_folder
+ padded_button = urwid.Padding(
+ self.search_button, align="center", width=("relative", 50)
+ ) # Tombol berada di tengah dengan lebar 50% dari total layar
+ padded_button = urwid.AttrMap(
+ padded_button, None, focus_map="reversed"
+ ) # Mengatur warna saat tombol difokuskan
+ urwid.connect_signal(
+ self.text_editor.base_widget, "change", self.set_focus_on_click, 0
+ )
+ urwid.connect_signal(
+ self.search_edit.base_widget, "change", self.set_focus_on_click, 1
+ )
-def get_latest_version(package_name):
- with Fetch() as req:
- response = req.get(
- f"https://pypi.org/pypi/{package_name}/json",
- max_retries=3,
- timeout=8,
- follow_redirects=True,
+ # Menu layout
+ self.main_menu_columns = urwid.Columns(
+ [
+ (
+ "weight",
+ 3,
+ urwid.AttrMap(search_limited, None, focus_map="reversed"),
+ ),
+ (
+ "weight",
+ 1,
+ urwid.AttrMap(padded_button, None, focus_map="reversed"),
+ ),
+ (
+ "weight",
+ 2,
+ urwid.AttrMap(self.quit_button, None, focus_map="reversed"),
+ ),
+ # (
+ # "weight",
+ # 4,
+ # urwid.AttrMap(urwid.Pile(menu_items), None, focus_map="reversed"),
+ # ),
+ ]
)
- data = response.json()
- if filter_json(data=data, keys=["info"]):
- return data["info"]["version"]
- return None
+ self.main_menu_pile = urwid.Pile([self.main_menu_columns])
-def check_update(package_name):
- installed_version = pkg_resources.get_distribution(package_name).version
- latest_version = get_latest_version(package_name)
+ # Layout
+ self.main_layout = urwid.Frame(
+ header=self.main_menu_pile,
+ body=urwid.Columns(
+ [
+ (
+ "weight",
+ 1,
+ urwid.LineBox(self.file_list_box, title="Directory Files"),
+ ),
+ (
+ "weight",
+ 1,
+ urwid.AttrMap(
+ self.Inspect_modules_from_package_Python,
+ None,
+ focus_map="reversed",
+ ),
+ ),
+ (
+ "weight",
+ 3,
+ urwid.LineBox(
+ urwid.Pile([self.text_editor_scrollable]), title="TextBox"
+ ),
+ ),
+ ]
+ ),
+ footer=urwid.Pile([self.status_msg_footer_text, self.main_footer_text]),
+ )
+ self.loop.set_alarm_in(timeout_v2(), self.update_uiV2)
+ self.system_alarm = self.loop.set_alarm_in(
+ timeout_v2() + 1,
+ lambda loop, user_data: self.system_usage(),
+ )
+ urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def create_modules_menus(self, listmodulename: list):
+ def create_button(module_name):
+ button = PlainButton(module_name)
+ urwid.connect_signal(button, "click", self.inspect_module, module_name)
+ return urwid.AttrMap(button, None, focus_map="reversed")
+
+ return [create_button(module) for module in listmodulename]
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def inspect_module(self, button, module_name):
+ result = self.module_package_Python.inspect_module(module_name)
+ if result:
+ if "module" in result.keys():
+ result_text = f"Module: {result['module']}\n\nGlobal Variables:\n"
+ result_text += ", ".join(result["variables"])
+ if result["classes"]:
+ result_text += "\n\nClass:\n"
+ for cls in result["classes"]:
+ if cls['name']:
+ result_text += f"Class: {cls['name']}\n"
+ result_text += " Variables:\n"
+ result_text += " " + "\n > ".join(cls["variables"]) + "\n\n"
+ if cls["functions"]:
+ result_text += " Function:\n"
+ for func in cls["functions"]:
+ result_text += f" > {func['name']}{func['params']}\n\n"
+ for funcs in result["functions"]:
+ if funcs['name']:
+ result_text += f"\nFunction: {funcs['name']}\n"
+ result_text += f" > {funcs['name']}{funcs['params']}\n\n"
+ self.Text_Deinspect_modules_from_package_Python.set_text(result_text)
+ else:
+ self.Text_Deinspect_modules_from_package_Python.set_text(
+ "Error inspecting module."
+ )
- if installed_version != latest_version:
- print(
- f"Package {package_name} can be updated from version {installed_version} to {latest_version}."
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def setup_popup(self, options, title, descrip: str = ""):
+ "Menyiapkan konten dan layout untuk menu popup dengan judul, deskripsi, dan opsi yang diberikan."
+ # Konten popup
+ menu_items = []
+ if descrip:
+ menu_items = [urwid.Text(descrip, align="center"), urwid.Divider("-")]
+
+ # Tambahkan opsi ke dalam menu popup
+ for option in options:
+ menu_items.append(option)
+
+ # Tambahkan tombol untuk menutup popup
+ menu_items.append(PlainButton("Close", on_press=self.close_popup))
+
+ # Buat listbox dari opsi yang sudah ada
+ popup_content = urwid.ListBox(urwid.SimpleFocusListWalker(menu_items))
+
+ # Tambahkan border dengan judul
+ self.popup = urwid.LineBox(popup_content, title=title)
+
+ def on_option_selected(self, button):
+ "Menangani pilihan opsi dari popup dengan menutup popup dan mengembalikan label opsi yang dipilih."
+ urwid.emit_signal(button, "click")
+ getbutton = button.get_label()
+ self.close_popup(None)
+ return getbutton
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def show_popup(self, title: str, descrip: str, menus: list):
+ "Menampilkan popup menu dengan judul, deskripsi, dan daftar opsi yang diberikan."
+ # Siapkan popup dengan judul, descrip, dan opsi
+ self.setup_popup(title=title, descrip=descrip, options=menus)
+
+ # Tentukan ukuran dan posisi popup
+ popup_width = 35
+ popup_height = 25
+ self.overlay_POPUP = urwid.Overlay(
+ self.popup,
+ self.main_layout,
+ "center",
+ ("relative", popup_width),
+ "middle",
+ ("relative", popup_height),
)
- else:
- print(f"Package {package_name} is up to date.")
+ self.loop.widget = self.overlay_POPUP
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def close_popup(self, button):
+ "Menutup popup menu dan mengembalikan tampilan ke layout utama."
+ self.overlay_POPUP = None
+ self.loop.widget = self.main_layout
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def get_file_list(self):
+ "Mengambil daftar file dan direktori di path saat ini, termasuk opsi untuk naik satu level di direktori jika bukan di direktori root."
+
+ files = []
+
+ if self.current_path != ".": # Cek apakah bukan di direktori root
+ button = PlainButton("...")
+ urwid.connect_signal(button, "click", self.go_up_directory)
+
+ files.append(urwid.AttrMap(button, None, focus_map="reversed"))
+
+ for f in os.listdir(f"{self.current_path}"):
+ if os.path.isdir(resolve_relative_path(self.current_path, f)):
+ f = f + "/"
+
+ button = PlainButton(f)
+
+ urwid.connect_signal(button, "click", self.open_file, f)
+
+ files.append(urwid.AttrMap(button, None, focus_map="reversed"))
+
+ return files
+
+ def handle_input(self, key):
+ "Menangani input keyboard dari pengguna untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy, paste, dan refresh UI."
+ if key in ("ctrl q", "ctrl Q", "esc"):
+ self.show_popup(
+ menus=[PlainButton("OK", on_press=lambda _x: self.quit_app())],
+ title="Confirm Quit",
+ descrip="Are you sure you Quit",
+ )
+
+ elif key in ("ctrl s", "ctrl S"):
+ # self.save_file()
+ self.show_popup(
+ menus=[
+ PlainButton(
+ "OK",
+ on_press=lambda _x: self.close_popup(None)
+ if self.save_file()
+ else None,
+ )
+ ],
+ title="Save File",
+ descrip="Are you sure you want to save the file changes",
+ )
+
+ elif key in ("ctrl d", "ctrl D"):
+ self.show_popup(
+ menus=[
+ PlainButton(
+ "OK",
+ on_press=lambda _x: self.close_popup(None)
+ if self.delete_file()
+ else None,
+ )
+ ],
+ title="Delete File",
+ descrip="Are you sure you want to delete the file",
+ )
+ elif key in ("ctrl z", "ctrl Z"):
+ self.undo_edit()
+ elif key in ("ctrl y", "ctrl Y"):
+ self.redo_edit()
+ elif key in ("ctrl c", "ctrl C"):
+ self.copy_text_to_clipboard()
+ elif key in ("ctrl v", "ctrl V"):
+ self.paste_text_from_clipboard()
+ elif key in ("ctrl r", "ctrl R"):
+ self.switch_to_secondary_layout()
+ elif key in ("f1", "ctrl e", "ctrl E"):
+ self.current_focus = 1 if self.current_focus == 0 else 0
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def get_current_edit(self):
+ "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)."
+ if self.current_focus == 0:
+ return self.text_editor.base_widget
+ elif self.current_focus == 1:
+ return self.search_edit.base_widget
+ return None
+
+ def set_focus_on_click(self, widget, new_edit_text, index):
+ "Mengatur fokus pada widget edit berdasarkan klik dan indeks."
+ self.current_focus = index
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def copy_text_to_clipboard(self):
+ "Menyalin teks dari widget edit yang sedang aktif ke clipboard."
+ current_edit = self.get_current_edit()
+ if current_edit:
+ if hasattr(current_edit, "edit_pos") and hasattr(
+ current_edit, "get_edit_text"
+ ):
+ self.status_msg_footer_text.set_text("Text copied to clipboard.")
+ cursor_position = current_edit.edit_pos
+ pyperclip.copy(
+ current_edit.get_edit_text()[cursor_position:]
+ or current_edit.get_edit_text()
+ )
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def paste_text_from_clipboard(self):
+ "Menempelkan teks dari clipboard ke widget edit yang sedang aktif."
+ pasted_text = pyperclip.paste() # Mengambil teks dari clipboard
+ current_edit = self.get_current_edit()
+ if current_edit:
+ if hasattr(current_edit, "edit_pos") and hasattr(
+ current_edit, "get_edit_text"
+ ):
+ current_text = (
+ current_edit.get_edit_text()
+ ) # Mendapatkan teks saat ini di widget Edit
+ cursor_position = (
+ current_edit.edit_pos
+ ) # Mendapatkan posisi kursor saat ini
+
+ # Membagi teks berdasarkan posisi kursor
+ text_before_cursor = current_text[:cursor_position]
+ text_after_cursor = current_text[cursor_position:]
+
+ # Gabungkan teks sebelum kursor, teks yang ditempelkan, dan teks setelah kursor
+ new_text = text_before_cursor + pasted_text + text_after_cursor
+
+ # Set teks baru dan sesuaikan posisi kursor
+ current_edit.set_edit_text(new_text)
+ current_edit.set_edit_pos(cursor_position + len(pasted_text))
+ self.status_msg_footer_text.set_text("Text paste from clipboard.")
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def go_up_directory(self, button):
+ "Naik satu level ke direktori atas dan memperbarui daftar file."
+ self.current_path = os.path.dirname(self.current_path)
+ self.file_list[:] = self.get_file_list()
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def open_file(self, button, file_name):
+ "Membuka file yang dipilih, membaca isinya, dan menampilkannya di text editor. Jika itu adalah direktori, berpindah ke direktori tersebut."
+ file_path = os.path.join(self.current_path, file_name)
+ _c, ext = os.path.splitext(file_path)
+ if os.path.isdir(file_path):
+ if validate_folder(file_path):
+ try:
+ sys.path.remove(self.current_path)
+ except:
+ pass
+ self.current_path = file_path
+ self.file_list[:] = self.get_file_list()
+ else:
+ self.status_msg_footer_text.set_text("Folder access denied!")
+ else:
+ if validate_folder(os.path.dirname(file_path)) and validate_file(
+ file_path, os.path.getsize(file_path) or 20, 6
+ ):
+ try:
+ with open(
+ file_path, "r+", encoding=sys.getfilesystemencoding()
+ ) as f:
+ content = f.read()
+ except UnicodeDecodeError:
+ with open(file_path, "r+", encoding="latin-1") as f:
+ content = f.read()
+ content = content.replace("\t", " " * 4)
+ self.undo_stack.append(content)
+ self.text_editor.set_edit_text(content)
+ self.current_file_name = file_name # Track the current file name
+
+ # if str(ext).lower() in ( ".pyx", ".pyz", ".py"):
+ # self.listmodules_from_package_Python[:] = self.modules_menus(self.current_path)
+
+ self.main_layout.body.contents[1][0].set_title(file_name)
+
+ else:
+ self.status_msg_footer_text.set_text("File access denied!")
+
+ if str(ext).lower().startswith((".pyx", ".pyz", ".py")) != True:
+ self.Text_Deinspect_modules_from_package_Python.set_text(
+ "Select a module to inspect."
+ )
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def save_file(self):
+ "Menyimpan perubahan yang dilakukan pada file saat ini dan mengembalikan status keberhasilan."
+ if self.current_file_name:
+ file_path = os.path.join(self.current_path, self.current_file_name)
+ try:
+ with open(file_path, "w+", encoding=sys.getfilesystemencoding()) as f:
+ f.write(self.text_editor.get_edit_text())
+ except:
+ with open(file_path, "w+", encoding="latin-1") as f:
+ f.write(self.text_editor.get_edit_text())
+ self.status_msg_footer_text.set_text("File saved successfully!")
+ return True
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def delete_file(self):
+ "Menghapus file yang dipilih dan memperbarui daftar file serta text editor dan mengembalikan status keberhasilan."
+ if self.current_file_name:
+ file_path = os.path.join(self.current_path, self.current_file_name)
+ if os.path.isfile(file_path):
+ os.remove(file_path)
+ self.text_editor.set_edit_text("")
+ self.file_list[:] = self.get_file_list()
+ self.status_msg_footer_text.set_text("File deleted successfully!")
+ self.current_file_name = None # Clear the current file name
+ else:
+ self.status_msg_footer_text.set_text("File does not exist!")
+ return True
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def save_undo_state(self):
+ "Menyimpan status saat ini dari text editor ke stack undo dan mengosongkan stack redo."
+ # Save the current content of the text editor for undo
+ current_text = self.text_editor.get_edit_text()
+ self.undo_stack.append(current_text)
+ self.redo_stack.clear() # Clear redo stack on new change
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def undo_edit(self):
+ "Melakukan undo terhadap perubahan terakhir pada text editor dengan mengembalikan status dari stack undo."
+ if self.undo_stack:
+ # Save the current state to redo stack
+ self.redo_stack.append(self.text_editor.get_edit_text())
+
+ # Restore the last state
+ last_state = self.undo_stack.pop()
+ self.text_editor.set_edit_text(last_state)
+ self.status_msg_footer_text.set_text("Undo performed.")
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def redo_edit(self):
+ "Melakukan redo terhadap perubahan terakhir yang diundo dengan mengembalikan status dari stack redo."
+ if self.redo_stack:
+ # Save the current state to undo stack
+ self.undo_stack.append(self.text_editor.get_edit_text())
+
+ # Restore the last redone state
+ last_state = self.redo_stack.pop()
+ self.text_editor.set_edit_text(last_state)
+ self.status_msg_footer_text.set_text("Redo performed.")
+
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def highlight_text(self, search_text):
+ text = self.text_editor.get_edit_text()
+ result = []
+ # Pisahkan teks menjadi sebelum, pencarian, dan sesudahnya
+ for x in findpositions(f"{search_text}", text):
+ if x:
+ _x = list(x)
+ result.append(str(_x[0][1][1]))
+ if result.__len__() > 8:
+ return "Total: {total} Pos: {posts}".format(
+ total=result.__len__(), posts=", ".join(result[:8]) + "..."
+ )
+ return "Total: {total} Pos: {posts}".format(
+ total=result.__len__(), posts=", ".join(result)
+ )
+
+ @complex_handle_errors(loggering=logging)
+ def in_search_(self, button):
+ "Mencari file atau folder berdasarkan input pencarian, membuka file jika ditemukan, atau memperbarui daftar file jika folder ditemukan."
+ search_query = self.search_edit.get_edit_text().replace("\\", "/").strip()
+ if search_query:
+ if ":" in search_query and not search_query.startswith("@[select]"):
+ if os.path.isfile(search_query):
+ dirname, file_name = os.path.dirname(
+ search_query
+ ), os.path.basename(search_query)
+ try:
+ with open(search_query, "r+", encoding="utf-8") as f:
+ content = f.read()
+ except UnicodeDecodeError:
+ with open(search_query, "r+", encoding="latin-1") as f:
+ content = f.read()
+ content = content.replace("\t", " " * 4)
+
+ self.undo_stack.append(content)
+ self.text_editor.set_edit_text(content)
+ self.current_file_name = file_name # Track the current file name
+ self.main_layout.body.contents[1][0].set_title(file_name)
+
+ elif os.path.isdir(search_query):
+ dirname = search_query
+ else:
+ x, _y = os.path.split(search_query)
+ if self.current_path.replace("\\", "/") == x.replace(
+ "\\", "/"
+ ) and os.path.isdir(x):
+ search_query = str(create_file_or_folder(search_query))
+ self.update_ui()
+ self.file_list[:] = self.get_file_list()
+
+ dirname = None
+
+ if dirname:
+ self.current_path = dirname
+ self.file_list[:] = self.get_file_list()
+ self.status_msg_footer_text.set_text(
+ f"Search results for '{search_query}'"
+ )
+ else:
+ search_resultsFile = [
+ f for f in os.listdir(self.current_path) if search_query in f
+ ]
+ search_resultsModule = [
+ module
+ for module in self.module_package_Python.curents
+ if search_query in module
+ ]
+ search_resultsHighlight_Text = self.highlight_text(search_query)
+
+ if search_resultsFile and search_resultsModule:
+ self.listmodules_from_package_Python[:] = self.create_modules_menus(
+ search_resultsModule
+ )
+ self.file_list[:] = self.create_file_list(search_resultsFile)
+ self.status_msg_footer_text.set_text(
+ f"Search results for '{search_query}'"
+ )
+ elif search_resultsFile:
+ self.file_list[:] = self.create_file_list(search_resultsFile)
+ self.status_msg_footer_text.set_text(
+ f"Search results for '{search_query}'"
+ )
+ else:
+ if search_resultsModule:
+ self.listmodules_from_package_Python[
+ :
+ ] = self.create_modules_menus(search_resultsModule)
+ self.file_list[:] = self.get_file_list()
+ self.status_msg_footer_text.set_text(
+ f"Search results for '{search_query}'"
+ )
+ elif search_resultsHighlight_Text and not search_query.startswith(
+ "@[files]"
+ ):
+ self.status_msg_footer_text.set_text(
+ f"Search results for '{search_query}' {search_resultsHighlight_Text}"
+ )
+ else:
+ if (
+ search_query.startswith("@[select]")
+ and search_query.find("[@rename]") > -1
+ ):
+ x = search_query.replace("@[select]", "", 1).split(
+ "[@rename]", 1
+ )
+ if x.__len__() == 2:
+ getREName = [
+ f
+ for f in os.listdir(self.current_path)
+ if x[0] in f
+ ]
+ if getREName.__len__() > 0:
+ oldfilesorfolder, newplace = [
+ os.path.join(self.current_path, getREName[0]),
+ os.path.join(self.current_path, x[1]),
+ ]
+ try:
+ os.rename(oldfilesorfolder, newplace)
+ self.status_msg_footer_text.set_text(
+ f"Rename {getREName[0]} success"
+ )
+ self.update_ui()
+ self.file_list[:] = self.get_file_list()
+ except:
+ pass
+ else:
+ self.status_msg_footer_text.set_text(
+ f"Search results for {search_query}"
+ )
+
+ else:
+ self.file_list[:] = self.get_file_list()
+ self.listmodules_from_package_Python[:] = self.create_modules_menus(
+ self.module_package_Python.curents
+ )
+ self.status_msg_footer_text.set_text("")
+
+ @complex_handle_errors(loggering=logging)
+ def create_file_list(self, files):
+ "Membuat daftar widget untuk file yang ditemukan sesuai hasil pencarian."
+ widgets = []
+ for f in files:
+ if os.path.isdir(os.path.join(self.current_path, f)):
+ f = f + "/"
+ button = PlainButton(f)
+ urwid.connect_signal(button, "click", self.open_file, f)
+ widgets.append(urwid.AttrMap(button, None, focus_map="reversed"))
+ return widgets
+
+ def system_usage(self):
+ "Memantau penggunaan CPU dan menampilkan peringatan jika konsumsi CPU tinggi."
+ timemming = timeout_v1()
+ if timemming > 0.87:
+ self.status_msg_footer_text.set_text("High CPU utilization alert")
+
+ def update_ui(self):
+ "Memperbarui tampilan UI aplikasi."
+ self.loop.draw_screen()
+
+ def update_uiV2(self, *args, **kwargs):
+ "Memperbarui tampilan UI aplikasi."
+ self.loop.set_alarm_in(timeout_v2(), self.update_uiV2)
+ self.loop.draw_screen()
+
+ def quit_app(self, button=None):
+ "Menghentikan aplikasi dan menghapus alarm sistem jika ada."
+ if self.system_alarm != None:
+ self.loop.remove_alarm(self.system_alarm) # Hentikan alarm
+ self.system_alarm = None
+ raise urwid.ExitMainLoop()
+
+ def run(self):
+ "Memulai loop utama urwid untuk menjalankan aplikasi."
+ self.loop.run()
+
+
+@complex_handle_errors(loggering=logging)
+def main(path: str):
+ app = SuperNano(start_path=path)
+ app.run()
+
+
+if __name__ == "__main__":
+ set_low_priority(os.getpid())
+ #########mendapatkan process terbaik tanpa membebani ram dan cpu
+
+ safe_executor = SafeProcessExecutor(
+ max_workers=2
+ ) #########mendapatkan process terbaik tanpa membebani cpu
+ safe_executor.submit(main, path=parse_args())
+ time.sleep(timeout_v2())
+ safe_executor.shutdown(
+ wait=True
+ ) ###mmenunggu process benar-benar berhenti tanpa memaksanya
+ rd = StreamFile(
+ file_path=fileloogiing,
+ buffer_size=os.path.getsize(fileloogiing) + 2,
+ print_delay=timeout_v2(),
+ ) #########mendapatkan process terbaik membaca file logging tanpa membebani cpu
+ for r in rd.readlines():
+ print(r)
+ rd.eraseFile() # membersihkan loggging
+ rd.close()
From b921a7941b1de84a1b11b6823079d1d9b143db6a Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:45:30 +0700
Subject: [PATCH 25/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index ad68349..a908921 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -703,7 +703,7 @@ def open_file(self, button, file_name):
# if str(ext).lower() in ( ".pyx", ".pyz", ".py"):
# self.listmodules_from_package_Python[:] = self.modules_menus(self.current_path)
- self.main_layout.body.contents[1][0].set_title(file_name)
+ self.main_layout.body.contents[2][0].set_title(file_name)
else:
self.status_msg_footer_text.set_text("File access denied!")
@@ -812,7 +812,7 @@ def in_search_(self, button):
self.undo_stack.append(content)
self.text_editor.set_edit_text(content)
self.current_file_name = file_name # Track the current file name
- self.main_layout.body.contents[1][0].set_title(file_name)
+ self.main_layout.body.contents[2][0].set_title(file_name)
elif os.path.isdir(search_query):
dirname = search_query
From d1c7b82015df1b5300a5a38f6512ab9f43360c0c Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:52:26 +0700
Subject: [PATCH 26/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index a908921..1f0a3e9 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -439,7 +439,10 @@ def setup_main_menu(self):
timeout_v2() + 1,
lambda loop, user_data: self.system_usage(),
)
- urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
+ try:
+ urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
+ except:
+ self.loop.widget = self.main_layout
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def create_modules_menus(self, listmodulename: list):
From 4d2c33f4fddf706b669bcf23df57999fdb458040 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:56:36 +0700
Subject: [PATCH 27/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 1277 ++++++++++++-------------------------------
1 file changed, 360 insertions(+), 917 deletions(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 1f0a3e9..3c2117f 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -1,983 +1,426 @@
-import urwid
-import pyperclip
-import os, sys, shutil, logging, time, threading, argparse
-from datetime import datetime
+import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading
try:
- from libs.helperegex import findpositions
- from libs.titlecommand import get_console_title, set_console_title
- from libs.cmd_filter import shorten_path, validate_folder
- from libs.errrorHandler import complex_handle_errors
- from libs.system_manajemen import set_low_priority, SafeProcessExecutor
- from libs.timeout import timeout_v2, timeout_v1
- from libs.filemanager import (
- StreamFile,
- validate_file,
- ModuleInspector,
- create_file_or_folder,
- resolve_relative_path,
- resolve_relative_path_v2,
- all_system_paths,
+ from .helperegex import (
+ searchmissing,
+ searching,
+ fullmacth,
+ rremovelist,
+ clean_string,
+ rreplace,
+ cleanstring,
)
+ from .cmd_filter import filter_json, safe_load_json
+ from .system_manajemen import set_low_priority, SafeProcessExecutor
+ from .timeout import timeout_v1, timeout_v2
+ from .https import Fetch
except:
try:
- from .helperegex import findpositions
- from .titlecommand import get_console_title, set_console_title
- from .cmd_filter import shorten_path, validate_folder
- from .errrorHandler import complex_handle_errors
- from .system_manajemen import set_low_priority, SafeProcessExecutor
- from .timeout import timeout_v2, timeout_v1
- from .filemanager import (
- StreamFile,
- validate_file,
- ModuleInspector,
- create_file_or_folder,
- resolve_relative_path,
- resolve_relative_path_v2,
- all_system_paths,
+ from helperegex import (
+ searchmissing,
+ searching,
+ fullmacth,
+ rremovelist,
+ clean_string,
+ rreplace,
+ cleanstring,
)
- except:
- from helperegex import findpositions
- from titlecommand import get_console_title, set_console_title
- from cmd_filter import shorten_path, validate_folder
- from errrorHandler import complex_handle_errors
+ from cmd_filter import filter_json, safe_load_json
from system_manajemen import set_low_priority, SafeProcessExecutor
- from timeout import timeout_v2, timeout_v1
- from filemanager import (
- StreamFile,
- validate_file,
- ModuleInspector,
- create_file_or_folder,
- resolve_relative_path,
- resolve_relative_path_v2,
- all_system_paths,
+ from timeout import timeout_v1, timeout_v2
+ from https import Fetch
+ except:
+ from libs.helperegex import (
+ searchmissing,
+ searching,
+ fullmacth,
+ rremovelist,
+ clean_string,
+ rreplace,
+ cleanstring,
)
+ from libs.cmd_filter import filter_json, safe_load_json
+ from libs.system_manajemen import set_low_priority, SafeProcessExecutor
+ from libs.timeout import timeout_v1, timeout_v2
+ from libs.https import Fetch
+if __name__ == "__main__":
+ set_low_priority(os.getpid())
-set_low_priority(os.getpid())
-#########mendapatkan process terbaik tanpa membebani ram dan cpu
-thisfolder, _x = all_system_paths
-__version__ = "2.1.0"
-fileloogiing = os.path.join(thisfolder, "cache", "file_browser.log").replace("\\", "/")
+script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")
+all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir]
-if not os.path.isfile(fileloogiing):
- open(fileloogiing, "a+")
-elif os.path.getsize(fileloogiing) > 0:
- with open(fileloogiing, "wb+") as f:
- f.truncate(0)
-for handler in logging.root.handlers[:]:
- logging.root.removeHandler(handler)
+class StreamFile:
+ def __init__(self, file_path: str, buffer_size: int = 8192, print_delay: float = 2):
+ """
+ Inisialisasi StreamFile untuk membaca file baris demi baris dengan delay dan menulis dengan buffer.
-logging.basicConfig(
- filename=fileloogiing,
- filemode="w",
- encoding=sys.getfilesystemencoding(),
- format="%(asctime)s, %(msecs)d %(name)s %(levelname)s [ %(filename)s-%(module)s-%(lineno)d ] : %(message)s",
- datefmt="%H:%M:%S",
- level=logging.ERROR,
-)
-logging.getLogger("urwid").disabled = True
-logger = logging.getLogger("urwid")
-for handler in logger.handlers[:]:
- logger.removeHandler(handler)
+ :param file_path: Path ke file yang akan dibaca atau ditulis.
+ :param buffer_size: Ukuran buffer sebelum data ditulis ke file.
+ :param print_delay: Waktu jeda (dalam detik) antara print setiap baris.
+ """
+ self.file_path = file_path
+ self.buffer_size = buffer_size or 0
+ self.print_delay = print_delay
+ self.buffer = bytearray()
+ def readlines(self):
+ """
+ Membaca file dengan buffer size dan menghasilkan setiap baris satu per satu dengan delay.
-def setTitle(title: str):
- """
- Fungsi setTitle bertugas untuk mengatur judul konsol (console title) berdasarkan parameter title yang diberikan.\n
- Jika inputan title memiliki panjang lebih dari 30 karakter maka potong karakternya
- """
- process = title
- Getitles = get_console_title()
- if os.path.isdir(process) or os.path.isfile(process):
- length = int(process.__len__() / 2)
- if length < 28:
- x = process.__len__()
- nexts = int(50 - x) - (x / 2)
- if nexts < 28:
- length = int((28 - nexts) + nexts)
- else:
- length = nexts
- elif length > 50:
- length = 28
- process = shorten_path(process, length)
-
- if Getitles.startswith("Win-SuperNano"):
- output = str("Win-SuperNano {titles}".format(titles=process))
- else:
- output = title
- set_console_title(output)
+ :yield: Baris dari file.
+ """
+ with open(self.file_path, "r+") as f:
+ buffer = self.buffer
+ while True:
+ chunk = f.read(self.buffer_size)
+ if not chunk:
+ break
-@complex_handle_errors(loggering=logging, nomessagesNormal=False)
-def parse_args():
- """
+ buffer.extend(
+ chunk.encode("utf-8")
+ ) # Encode chunk to bytes if necessary
- Fungsi parse_args bertugas untuk mendapatkan\menangkap argument konsol (console title) yang diberikan oleh user.\n
+ while b"\n" in buffer:
+ line, buffer = buffer.split(b"\n", 1)
+ yield line.decode("utf-8")
+ time.sleep(self.print_delay)
- """
+ if buffer:
+ yield buffer.decode("utf-8")
+ self.buffer_size = 0
- parser = argparse.ArgumentParser(
- description="An extension on nano for editing directories in CLI."
- )
+ def write(self, data):
+ """
+ Menulis data ke buffer dan secara otomatis menulis ke file ketika buffer penuh.
- parser.add_argument(
- "path",
- default=os.path.split(thisfolder)[0],
- nargs="?",
- type=str,
- help="Target file or directory to edit.",
- )
+ :param data: Data yang akan ditulis ke buffer.
+ """
+ self.buffer.extend(data)
+ while len(self.buffer) >= self.buffer_size:
+ with open(self.file_path, "ab+") as f:
+ f.write(self.buffer[: self.buffer_size])
+ self.buffer = self.buffer[self.buffer_size :]
- args = parser.parse_args()
+ def writelines(self, lines):
+ """
+ Menulis baris-baris data ke file dengan delay antara setiap baris.
- path = resolve_relative_path(args.path, "") or "."
+ :param lines: List atau generator yang menghasilkan baris-baris data untuk ditulis.
+ """
+ for line in lines:
+ self.write(line.encode("utf-8"))
+ time.sleep(self.print_delay + timeout_v1())
+ self.close() # Memastikan buffer ditulis dan ditutup setelah penulisan selesai
- if os.path.exists(path):
- if validate_folder(path=path):
- pass
+ def eraseFile(self):
+ with open(self.file_path, "rb+") as f:
+ f.truncate(0)
+ def close(self):
+ """
+ Menulis sisa data di buffer ke file dan membersihkan buffer.
+ """
+ if self.buffer and self.buffer_size:
+ with open(self.file_path, "ab+") as f:
+ f.write(self.buffer)
+ self.buffer.clear()
else:
- logging.error(f"ERROR - {path} path cannot access")
-
- exit()
+ pass
- else:
- logging.error(f"ERROR - {path} path does not exist")
- exit()
+class ModuleInspector:
+ def __init__(self):
+ self.modules = self.getsys_module()
+ self.curents = self.modules
+ self.curentpath = sys.path.copy()
+ self.modulepathnow = []
- return resolve_relative_path_v2(path).replace("\\", "/")
+ def getsys_module(self):
+ return sorted(
+ [
+ module.name
+ for module in pkgutil.iter_modules([x for x in sys.path if x])
+ if not module.name.strip().startswith("~")
+ and not module.name.strip().startswith("__pycache__")
+ ]
+ )
+
+ def get_module(self, paths:list=[]):
+ def getmodules(path:list, result:list):
+ result.extend(sorted(
+ [
+ module.name
+ for module in pkgutil.iter_modules(path)
+ if not module.name.strip().startswith("~")
+ and not module.name.strip().startswith("__pycache__")
+ ]
+ ))
+ threads, result = [[], self.curents]
+ if paths.__len__()<1:
+ paths = [os.getcwd()]
+ else:
+ pass
+
-class PlainButton(urwid.Button):
- """
- Class PlainButton bertugas untuk mengkoustomisasi button dan menghilangkan karakter < dan >.\n
- """
+ for path in paths:
+ thread = threading.Thread(target=getmodules, args=([path], result))
+ thread.start()
+ threads.append(thread)
- button_left = urwid.Text("")
- button_right = urwid.Text("")
-
-
-class EditableButton(urwid.WidgetWrap):
- def __init__(self, label, on_save):
- self.label = label
- self.on_save = on_save
- self.button = PlainButton(label)
- self.last_click_time = 0
- urwid.connect_signal(self.button, "click", self.on_click)
- self._w = self.button
-
- def on_click(self, button):
- current_time = time.time()
- # Check for double click within 0.3 seconds
- if current_time - self.last_click_time < 0.3:
- self.edit_text(button)
- self.last_click_time = current_time
-
- def edit_text(self, button):
- self.edit = urwid.Edit(multiline=False, edit_text=self.label)
- urwid.connect_signal(self.edit, "change", self.on_change)
- self._w = urwid.AttrMap(self.edit, None, focus_map="reversed")
-
- def on_change(self, edit, new_text):
- self.label = new_text
-
- def keypress(self, size, key):
- if isinstance(self._w.base_widget, urwid.Edit):
- if key == "enter":
- self.save_and_restore()
- else:
- return self._w.keypress(size, key)
- else:
- return super().keypress(size, key)
+ for thread in threads:
+ thread.join()
- def save_and_restore(self):
- self.on_save(self.label)
- self.button = PlainButton(self.label)
- urwid.connect_signal(self.button, "click", self.on_click)
- self._w = self.button
+ self.modulepathnow = paths
+ return result
+
+ def list_classes(self, module):
+ try:
+ imported_module = importlib.import_module(module)
+ classes = [
+ obj
+ for name, obj in inspect.getmembers(imported_module)
+ if inspect.isclass(obj)
+ ]
+ if not classes:
+ pass
+ return classes
+ except Exception as e:
+ return []
+
+ def get_class_details(self, cls):
+ details = {"name": cls.__name__, "variables": [], "functions": []}
+
+ for name, obj in inspect.getmembers(cls):
+ if inspect.isfunction(obj):
+ func_details = {"name": name, "params": str(inspect.signature(obj))}
+ details["functions"].append(func_details)
+ elif not name.startswith("__") and not inspect.ismodule(obj):
+ details["variables"].append(name)
+
+ return details
+
+ def get_function_detail(self, module):
+ details = []
+ try:
+ for name, obj in inspect.getmembers(importlib.import_module(module)):
+ if inspect.isfunction(obj):
+ func_details = {"name": name, "params": str(inspect.signature(obj))}
+ details.append(func_details)
+ except:
+ pass
+ return details
-class ClipboardTextBox(urwid.Edit):
- def keypress(self, size, key):
- if key == "ctrl c":
- self.copy_to_clipboard()
- elif key == "ctrl v":
- self.paste_from_clipboard()
+ def get_global_variables(self, module):
+ try:
+ imported_module = importlib.import_module(module)
+ # global_vars = {name: self.serialize_value(value) for name, value in vars(imported_module).items()
+ # if not (inspect.isclass(value) or inspect.isfunction(value)) and not name.startswith('__')}
+ global_vars = [
+ name
+ for name, value in vars(imported_module).items()
+ if not (inspect.isclass(value) or inspect.isfunction(value))
+ and not name.startswith("__")
+ ]
+ return global_vars
+ except Exception as e:
+ return []
+
+ def serialize_value(self, value):
+ """Serialize values for JSON compatibility."""
+ if isinstance(value, (int, float, str, bool, list, dict)):
+ return value
+ elif callable(value):
+ return f"Function: {value.__name__}"
else:
- return super().keypress(size, key)
+ return str(value) # Convert other types to string
- def copy_to_clipboard(self):
- self.clipboard = self.get_edit_text()
-
- def paste_from_clipboard(self):
- if hasattr(self, "clipboard"):
- cursor_pos = self.edit_pos
- text = self.get_edit_text()
- self.set_edit_text(text[:cursor_pos] + self.clipboard + text[cursor_pos:])
- self.edit_pos = cursor_pos + len(self.clipboard)
+ def inspect_module(self, module_name):
+ if self.modulepathnow.__len__()>=1:
+ sys.path.extend(self.modulepathnow)
+ self.modulepathnow = []
+ try:
+ classes = self.list_classes(module_name)
+ global_vars = self.get_global_variables(module_name)
+ result = {
+ "module": module_name,
+ "variables": global_vars,
+ "classes": [],
+ "functions": self.get_function_detail(module_name),
+ }
+ for cls in classes:
+ class_details = self.get_class_details(cls)
+ result["classes"].append(class_details)
-class SuperNano:
- """
- Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses].
+ # Convert the result to JSON and print it
- Pembuat: Ramsyan Tungga Kiansantang (ID) | Github: LcfherShell
+ sys.path = self.curentpath
+ return result
+ except Exception as e:
+ sys.path = self.curentpath
+ return None
- Tanggal dibuat: 21 Agustus 2024
- Jika ada bug silahkan kunjungi git yang telah tertera diatas
+def create_file_or_folder(path: str) -> str:
"""
+ Membuat file atau folder di path yang diberikan.
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def __init__(self, start_path="."):
- "Mengatur path awal, judul aplikasi, widget, dan layout utama. Juga mengatur alarm untuk memuat menu utama dan memulai loop aplikasi."
- self.current_path = start_path
- self.current_pathx = self.current_path
-
- self.current_file_name = None # Track current file name
- self.undo_stack, self.redo_stack = [[], []] # Stack for undo # Stack for redo
- self.overlay_POPUP = None # Overlay untuk popup
- self.module_package_Python = ModuleInspector() # memuat module python
-
- # Set title
- setTitle("Win-SuperNano v{version}".format(version=__version__))
-
- # Create widgets
- """
- 1.loading menu
- 2. main menu: search, list file or folder, and inspect module python
- """
-
- ######Create widgets modulepython menu
- def create_button(module_name):
- button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, module_name)
- return urwid.AttrMap(button, None, focus_map="reversed")
-
- self.listmodules_from_package_Python = urwid.SimpleFocusListWalker(
- [
- create_button(module)
- for module in self.module_package_Python.get_module(sys.path)
- ]
- )
- # Footer text and ListBox for scrolling
- self.Text_Deinspect_modules_from_package_Python = urwid.Text(
- "Select a module to inspect."
- )
- MenuText_Inspect_modules_from_package_Python = urwid.ListBox(
- urwid.SimpleFocusListWalker(
- [self.Text_Deinspect_modules_from_package_Python]
- )
- )
- Box_Deinspect_modules_from_package_Python = urwid.BoxAdapter(
- MenuText_Inspect_modules_from_package_Python, 14
- ) # Set max height for the footer
- # Use a Frame to wrap the main content and footer
- self.Inspect_modules_from_package_Python = urwid.Frame(
- body=urwid.LineBox(
- urwid.ListBox(self.listmodules_from_package_Python),
- title="Python Modules",
- ),
- footer=Box_Deinspect_modules_from_package_Python,
- )
-
- ###Create widgets loading menu
- self.title_loading_widget = urwid.Text(
- "Win-SuperNano v{version} CopyRight: LcfherShell@{year}\n".format(
- version=__version__, year=datetime.now().year
- ),
- align="center",
- )
- self.loading_widget = urwid.Text("Loading, please wait...", align="center")
- self.main_layout = urwid.Filler(
- urwid.Pile([self.title_loading_widget, self.loading_widget]),
- valign="middle",
- )
-
- # Create main menu
- self.main_menu_columns = urwid.Columns([])
- self.main_menu_pile = urwid.Pile([self.main_menu_columns])
- self.status_msg_footer_text = urwid.Text(
- "Press ctrl + q to exit, Arrow keys to navigate"
- )
- self.main_footer_text = urwid.Text(
- "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+R : Refresh UI ESC: Quit "
- )
+ Args:
+ path (str): Path lengkap tempat file atau folder akan dibuat.
- # Event loop
- self.loop = urwid.MainLoop(self.main_layout, unhandled_input=self.handle_input)
- self.loading_alarm = self.loop.set_alarm_in(
- round(timeout_v1() * timeout_v2(), 1) + 1,
- lambda loop, user_data: self.load_main_menu(),
- )
- self.system_alarm = None
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def load_main_menu(self):
- "Menyiapkan dan menampilkan menu utama setelah periode loading, dan menghapus alarm loading."
- # self.loading_widget.set_text("Press key R")
- set_low_priority(os.getpid())
- self.loop.remove_alarm(self.loading_alarm) # Hentikan alarm
- self.loading_alarm = None
- self.switch_to_secondary_layout()
-
- def switch_to_secondary_layout(self):
- "Mengubah layout aplikasi ke menu utama yang telah disiapkan."
- self.setup_main_menu()
- if self.loading_alarm != None:
- self.loop.remove_alarm(
- self.loading_alarm
- ) # Hentikan alarm loading jika masih ada
- self.loading_alarm = None
- self.loop.widget = self.main_layout
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def setup_main_menu(self):
- "Menyiapkan dan mengatur widget untuk menu utama, termasuk daftar file, editor teks, dan tombol-tombol fungsional. Mengatur layout untuk tampilan aplikasi."
- # Define widgets
- self.file_list = urwid.SimpleFocusListWalker(self.get_file_list())
- self.file_list_box = urwid.ListBox(self.file_list)
- self.text_editor = urwid.Edit(multiline=True)
- self.current_focus = 0 # 0 for textbox1, 1 for textbox2
- # Wrap text_editor with BoxAdapter for scrollable content
- self.text_editor_scrollable = urwid.LineBox(
- urwid.Filler(self.text_editor, valign="top")
- )
-
- # Define menu widgets
- self.quit_button = PlainButton("Quit", align="center")
- urwid.connect_signal(self.quit_button, "click", self.quit_app)
-
- self.search_edit = urwid.Edit(
- "Search, Rename or Create: ", multiline=False, align="left"
- )
- search_limited = urwid.BoxAdapter(
- urwid.Filler(self.search_edit, valign="top"), height=1
- )
-
- self.search_button = PlainButton("Execute", align="center")
- urwid.connect_signal(self.search_button, "click", self.in_search_)
-
- padded_button = urwid.Padding(
- self.search_button, align="center", width=("relative", 50)
- ) # Tombol berada di tengah dengan lebar 50% dari total layar
- padded_button = urwid.AttrMap(
- padded_button, None, focus_map="reversed"
- ) # Mengatur warna saat tombol difokuskan
+ Returns:
+ str: Pesan konfirmasi yang menunjukkan apakah file atau folder berhasil dibuat.
+ """
- urwid.connect_signal(
- self.text_editor.base_widget, "change", self.set_focus_on_click, 0
- )
- urwid.connect_signal(
- self.search_edit.base_widget, "change", self.set_focus_on_click, 1
- )
+ if not path:
+ return "Path is empty."
- # Menu layout
- self.main_menu_columns = urwid.Columns(
- [
- (
- "weight",
- 3,
- urwid.AttrMap(search_limited, None, focus_map="reversed"),
- ),
- (
- "weight",
- 1,
- urwid.AttrMap(padded_button, None, focus_map="reversed"),
- ),
- (
- "weight",
- 2,
- urwid.AttrMap(self.quit_button, None, focus_map="reversed"),
- ),
- # (
- # "weight",
- # 4,
- # urwid.AttrMap(urwid.Pile(menu_items), None, focus_map="reversed"),
- # ),
- ]
- )
+ if os.path.isdir(path):
+ return f"The folder '{os.path.basename(path)}' already exists."
- self.main_menu_pile = urwid.Pile([self.main_menu_columns])
+ if os.path.isfile(path):
+ return f"The file '{os.path.basename(path)}' already exists."
- # Layout
- self.main_layout = urwid.Frame(
- header=self.main_menu_pile,
- body=urwid.Columns(
- [
- (
- "weight",
- 1,
- urwid.LineBox(self.file_list_box, title="Directory Files"),
- ),
- (
- "weight",
- 1,
- urwid.AttrMap(
- self.Inspect_modules_from_package_Python,
- None,
- focus_map="reversed",
- ),
- ),
- (
- "weight",
- 3,
- urwid.LineBox(
- urwid.Pile([self.text_editor_scrollable]), title="TextBox"
- ),
- ),
- ]
- ),
- footer=urwid.Pile([self.status_msg_footer_text, self.main_footer_text]),
- )
- self.loop.set_alarm_in(timeout_v2(), self.update_uiV2)
- self.system_alarm = self.loop.set_alarm_in(
- timeout_v2() + 1,
- lambda loop, user_data: self.system_usage(),
- )
+ folder, filename = os.path.split(path)
+ if "." in os.path.basename(path) and os.path.exists(folder):
+ # Membuat file
try:
- urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
- except:
- self.loop.widget = self.main_layout
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def create_modules_menus(self, listmodulename: list):
- def create_button(module_name):
- button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, module_name)
- return urwid.AttrMap(button, None, focus_map="reversed")
-
- return [create_button(module) for module in listmodulename]
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def inspect_module(self, button, module_name):
- result = self.module_package_Python.inspect_module(module_name)
- if result:
- if "module" in result.keys():
- result_text = f"Module: {result['module']}\n\nGlobal Variables:\n"
- result_text += ", ".join(result["variables"])
- if result["classes"]:
- result_text += "\n\nClass:\n"
- for cls in result["classes"]:
- if cls['name']:
- result_text += f"Class: {cls['name']}\n"
- result_text += " Variables:\n"
- result_text += " " + "\n > ".join(cls["variables"]) + "\n\n"
- if cls["functions"]:
- result_text += " Function:\n"
- for func in cls["functions"]:
- result_text += f" > {func['name']}{func['params']}\n\n"
- for funcs in result["functions"]:
- if funcs['name']:
- result_text += f"\nFunction: {funcs['name']}\n"
- result_text += f" > {funcs['name']}{funcs['params']}\n\n"
- self.Text_Deinspect_modules_from_package_Python.set_text(result_text)
- else:
- self.Text_Deinspect_modules_from_package_Python.set_text(
- "Error inspecting module."
+ if folder and not os.path.exists(folder):
+ return f"Failed to create the file '{filename}'"
+ with open(path, "wb") as f:
+ pass # Membuat file kosong
+ return f"The file '{filename}' has been successfully created."
+ except Exception as e:
+ return f"Failed to create the file '{filename}'"
+ elif os.path.exists(folder) and folder:
+ # Membuat folder
+ try:
+ os.makedirs(path)
+ return (
+ f"The folder '{os.path.basename(path)}' has been successfully created."
)
+ except FileExistsError:
+ return f"The folder '{os.path.basename(path)}' already exists."
+ except Exception as e:
+ return f"Failed to create the folder '{os.path.basename(path)}'."
+ else:
+ return "Something happened."
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def setup_popup(self, options, title, descrip: str = ""):
- "Menyiapkan konten dan layout untuk menu popup dengan judul, deskripsi, dan opsi yang diberikan."
- # Konten popup
- menu_items = []
- if descrip:
- menu_items = [urwid.Text(descrip, align="center"), urwid.Divider("-")]
-
- # Tambahkan opsi ke dalam menu popup
- for option in options:
- menu_items.append(option)
-
- # Tambahkan tombol untuk menutup popup
- menu_items.append(PlainButton("Close", on_press=self.close_popup))
-
- # Buat listbox dari opsi yang sudah ada
- popup_content = urwid.ListBox(urwid.SimpleFocusListWalker(menu_items))
-
- # Tambahkan border dengan judul
- self.popup = urwid.LineBox(popup_content, title=title)
-
- def on_option_selected(self, button):
- "Menangani pilihan opsi dari popup dengan menutup popup dan mengembalikan label opsi yang dipilih."
- urwid.emit_signal(button, "click")
- getbutton = button.get_label()
- self.close_popup(None)
- return getbutton
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def show_popup(self, title: str, descrip: str, menus: list):
- "Menampilkan popup menu dengan judul, deskripsi, dan daftar opsi yang diberikan."
- # Siapkan popup dengan judul, descrip, dan opsi
- self.setup_popup(title=title, descrip=descrip, options=menus)
-
- # Tentukan ukuran dan posisi popup
- popup_width = 35
- popup_height = 25
- self.overlay_POPUP = urwid.Overlay(
- self.popup,
- self.main_layout,
- "center",
- ("relative", popup_width),
- "middle",
- ("relative", popup_height),
- )
- self.loop.widget = self.overlay_POPUP
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def close_popup(self, button):
- "Menutup popup menu dan mengembalikan tampilan ke layout utama."
- self.overlay_POPUP = None
- self.loop.widget = self.main_layout
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def get_file_list(self):
- "Mengambil daftar file dan direktori di path saat ini, termasuk opsi untuk naik satu level di direktori jika bukan di direktori root."
-
- files = []
-
- if self.current_path != ".": # Cek apakah bukan di direktori root
- button = PlainButton("...")
-
- urwid.connect_signal(button, "click", self.go_up_directory)
-
- files.append(urwid.AttrMap(button, None, focus_map="reversed"))
- for f in os.listdir(f"{self.current_path}"):
- if os.path.isdir(resolve_relative_path(self.current_path, f)):
- f = f + "/"
+def is_binary_file(file_path):
+ """
+ Menentukan apakah file adalah file biner atau bukan.
- button = PlainButton(f)
+ Args:
+ file_path (str): Path ke file yang akan diperiksa.
- urwid.connect_signal(button, "click", self.open_file, f)
+ Returns:
+ bool: True jika file adalah file biner, False jika bukan.
+ """
+ try:
+ with open(file_path, "rb") as file:
+ chunk = file.read(1024) # Membaca bagian pertama file (1KB)
+ # Cek apakah file memiliki karakter yang tidak biasa untuk teks
+ if b"\0" in chunk: # Null byte adalah indikator umum dari file biner
+ return True
+ # Cek apakah file sebagian besar berisi karakter teks (misalnya ASCII)
+ text_chars = b"".join([bytes((i,)) for i in range(32, 127)]) + b"\n\r\t\b"
+ non_text_chars = chunk.translate(None, text_chars)
+ if (
+ len(non_text_chars) / len(chunk) > 0.30
+ ): # Jika lebih dari 30% karakter non-teks
+ return True
+ return False
+ except Exception as e:
+ return False
+
+def validate_file(file_path, max_size_mb=100, max_read_time=2):
+ try:
+ # Periksa ukuran file
+ file_size = os.path.getsize(file_path)
+ if file_size > max_size_mb * 1024 * 1024:
+ return False
+
+ # Mulai waktu pembacaan
+ start_time = time.time()
+
+ # Baca file
+ with open(file_path, "rb") as f:
+ # Baca bagian pertama file untuk memeriksa apakah file biner
+ first_bytes = f.read(1024)
+ if b"\x00" in first_bytes:
+ return False
+
+ # Lanjutkan membaca file
+ while f.read(1024):
+ # Periksa waktu yang telah digunakan untuk membaca
+ elapsed_time = time.time() - start_time
+ if elapsed_time > max_read_time:
+ return False
+
+ # Jika semua pemeriksaan lolos, file valid
+ return True
- files.append(urwid.AttrMap(button, None, focus_map="reversed"))
+ except Exception as e:
+ return False
- return files
- def handle_input(self, key):
- "Menangani input keyboard dari pengguna untuk berbagai tindakan seperti keluar, menyimpan, menghapus, undo, redo, copy, paste, dan refresh UI."
- if key in ("ctrl q", "ctrl Q", "esc"):
- self.show_popup(
- menus=[PlainButton("OK", on_press=lambda _x: self.quit_app())],
- title="Confirm Quit",
- descrip="Are you sure you Quit",
- )
+def check_class_in_package(package_name, class_name):
+ try:
+ # Import the package
+ package = importlib.import_module(package_name)
+ # Cek apakah kelas ada di dalam modul
+ if hasattr(package, class_name):
+ cls = getattr(package, class_name)
+ # Pastikan itu adalah kelas, bukan atribut atau fungsi
+ if inspect.isclass(cls):
+ return True, "ClassFound"
+ return False, "ClassNotFound"
+ except ModuleNotFoundError:
+ return False, "ModuleNotFoundError"
+
+
+def resolve_relative_path(current_path: str, relative_path: str) -> str:
+ # Menggabungkan current_path dengan relative_path (misalnya "../")
+ target_path: str = os.path.normpath(os.path.join(current_path, relative_path))
+ return target_path
+
+
+def resolve_relative_path_v2(path: str) -> str:
+ target_folder: str = resolve_relative_path(
+ os.getcwd().replace("\\", "/"), path.replace("\\", "/")
+ )
+ return target_folder
- elif key in ("ctrl s", "ctrl S"):
- # self.save_file()
- self.show_popup(
- menus=[
- PlainButton(
- "OK",
- on_press=lambda _x: self.close_popup(None)
- if self.save_file()
- else None,
- )
- ],
- title="Save File",
- descrip="Are you sure you want to save the file changes",
- )
- elif key in ("ctrl d", "ctrl D"):
- self.show_popup(
- menus=[
- PlainButton(
- "OK",
- on_press=lambda _x: self.close_popup(None)
- if self.delete_file()
- else None,
- )
- ],
- title="Delete File",
- descrip="Are you sure you want to delete the file",
- )
- elif key in ("ctrl z", "ctrl Z"):
- self.undo_edit()
- elif key in ("ctrl y", "ctrl Y"):
- self.redo_edit()
- elif key in ("ctrl c", "ctrl C"):
- self.copy_text_to_clipboard()
- elif key in ("ctrl v", "ctrl V"):
- self.paste_text_from_clipboard()
- elif key in ("ctrl r", "ctrl R"):
- self.switch_to_secondary_layout()
- elif key in ("f1", "ctrl e", "ctrl E"):
- self.current_focus = 1 if self.current_focus == 0 else 0
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def get_current_edit(self):
- "Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)."
- if self.current_focus == 0:
- return self.text_editor.base_widget
- elif self.current_focus == 1:
- return self.search_edit.base_widget
- return None
-
- def set_focus_on_click(self, widget, new_edit_text, index):
- "Mengatur fokus pada widget edit berdasarkan klik dan indeks."
- self.current_focus = index
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def copy_text_to_clipboard(self):
- "Menyalin teks dari widget edit yang sedang aktif ke clipboard."
- current_edit = self.get_current_edit()
- if current_edit:
- if hasattr(current_edit, "edit_pos") and hasattr(
- current_edit, "get_edit_text"
- ):
- self.status_msg_footer_text.set_text("Text copied to clipboard.")
- cursor_position = current_edit.edit_pos
- pyperclip.copy(
- current_edit.get_edit_text()[cursor_position:]
- or current_edit.get_edit_text()
- )
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def paste_text_from_clipboard(self):
- "Menempelkan teks dari clipboard ke widget edit yang sedang aktif."
- pasted_text = pyperclip.paste() # Mengambil teks dari clipboard
- current_edit = self.get_current_edit()
- if current_edit:
- if hasattr(current_edit, "edit_pos") and hasattr(
- current_edit, "get_edit_text"
- ):
- current_text = (
- current_edit.get_edit_text()
- ) # Mendapatkan teks saat ini di widget Edit
- cursor_position = (
- current_edit.edit_pos
- ) # Mendapatkan posisi kursor saat ini
-
- # Membagi teks berdasarkan posisi kursor
- text_before_cursor = current_text[:cursor_position]
- text_after_cursor = current_text[cursor_position:]
-
- # Gabungkan teks sebelum kursor, teks yang ditempelkan, dan teks setelah kursor
- new_text = text_before_cursor + pasted_text + text_after_cursor
-
- # Set teks baru dan sesuaikan posisi kursor
- current_edit.set_edit_text(new_text)
- current_edit.set_edit_pos(cursor_position + len(pasted_text))
- self.status_msg_footer_text.set_text("Text paste from clipboard.")
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def go_up_directory(self, button):
- "Naik satu level ke direktori atas dan memperbarui daftar file."
- self.current_path = os.path.dirname(self.current_path)
- self.file_list[:] = self.get_file_list()
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def open_file(self, button, file_name):
- "Membuka file yang dipilih, membaca isinya, dan menampilkannya di text editor. Jika itu adalah direktori, berpindah ke direktori tersebut."
- file_path = os.path.join(self.current_path, file_name)
- _c, ext = os.path.splitext(file_path)
- if os.path.isdir(file_path):
- if validate_folder(file_path):
- try:
- sys.path.remove(self.current_path)
- except:
- pass
- self.current_path = file_path
- self.file_list[:] = self.get_file_list()
- else:
- self.status_msg_footer_text.set_text("Folder access denied!")
- else:
- if validate_folder(os.path.dirname(file_path)) and validate_file(
- file_path, os.path.getsize(file_path) or 20, 6
- ):
- try:
- with open(
- file_path, "r+", encoding=sys.getfilesystemencoding()
- ) as f:
- content = f.read()
- except UnicodeDecodeError:
- with open(file_path, "r+", encoding="latin-1") as f:
- content = f.read()
- content = content.replace("\t", " " * 4)
- self.undo_stack.append(content)
- self.text_editor.set_edit_text(content)
- self.current_file_name = file_name # Track the current file name
-
- # if str(ext).lower() in ( ".pyx", ".pyz", ".py"):
- # self.listmodules_from_package_Python[:] = self.modules_menus(self.current_path)
-
- self.main_layout.body.contents[2][0].set_title(file_name)
-
- else:
- self.status_msg_footer_text.set_text("File access denied!")
-
- if str(ext).lower().startswith((".pyx", ".pyz", ".py")) != True:
- self.Text_Deinspect_modules_from_package_Python.set_text(
- "Select a module to inspect."
- )
+def get_latest_version(package_name):
+ with Fetch() as req:
+ response = req.get(
+ f"https://pypi.org/pypi/{package_name}/json",
+ max_retries=3,
+ timeout=8,
+ follow_redirects=True,
+ )
+ data = response.json()
+ if filter_json(data=data, keys=["info"]):
+ return data["info"]["version"]
+ return None
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def save_file(self):
- "Menyimpan perubahan yang dilakukan pada file saat ini dan mengembalikan status keberhasilan."
- if self.current_file_name:
- file_path = os.path.join(self.current_path, self.current_file_name)
- try:
- with open(file_path, "w+", encoding=sys.getfilesystemencoding()) as f:
- f.write(self.text_editor.get_edit_text())
- except:
- with open(file_path, "w+", encoding="latin-1") as f:
- f.write(self.text_editor.get_edit_text())
- self.status_msg_footer_text.set_text("File saved successfully!")
- return True
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def delete_file(self):
- "Menghapus file yang dipilih dan memperbarui daftar file serta text editor dan mengembalikan status keberhasilan."
- if self.current_file_name:
- file_path = os.path.join(self.current_path, self.current_file_name)
- if os.path.isfile(file_path):
- os.remove(file_path)
- self.text_editor.set_edit_text("")
- self.file_list[:] = self.get_file_list()
- self.status_msg_footer_text.set_text("File deleted successfully!")
- self.current_file_name = None # Clear the current file name
- else:
- self.status_msg_footer_text.set_text("File does not exist!")
- return True
+def check_update(package_name):
+ installed_version = pkg_resources.get_distribution(package_name).version
+ latest_version = get_latest_version(package_name)
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def save_undo_state(self):
- "Menyimpan status saat ini dari text editor ke stack undo dan mengosongkan stack redo."
- # Save the current content of the text editor for undo
- current_text = self.text_editor.get_edit_text()
- self.undo_stack.append(current_text)
- self.redo_stack.clear() # Clear redo stack on new change
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def undo_edit(self):
- "Melakukan undo terhadap perubahan terakhir pada text editor dengan mengembalikan status dari stack undo."
- if self.undo_stack:
- # Save the current state to redo stack
- self.redo_stack.append(self.text_editor.get_edit_text())
-
- # Restore the last state
- last_state = self.undo_stack.pop()
- self.text_editor.set_edit_text(last_state)
- self.status_msg_footer_text.set_text("Undo performed.")
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def redo_edit(self):
- "Melakukan redo terhadap perubahan terakhir yang diundo dengan mengembalikan status dari stack redo."
- if self.redo_stack:
- # Save the current state to undo stack
- self.undo_stack.append(self.text_editor.get_edit_text())
-
- # Restore the last redone state
- last_state = self.redo_stack.pop()
- self.text_editor.set_edit_text(last_state)
- self.status_msg_footer_text.set_text("Redo performed.")
-
- @complex_handle_errors(loggering=logging, nomessagesNormal=False)
- def highlight_text(self, search_text):
- text = self.text_editor.get_edit_text()
- result = []
- # Pisahkan teks menjadi sebelum, pencarian, dan sesudahnya
- for x in findpositions(f"{search_text}", text):
- if x:
- _x = list(x)
- result.append(str(_x[0][1][1]))
- if result.__len__() > 8:
- return "Total: {total} Pos: {posts}".format(
- total=result.__len__(), posts=", ".join(result[:8]) + "..."
- )
- return "Total: {total} Pos: {posts}".format(
- total=result.__len__(), posts=", ".join(result)
+ if installed_version != latest_version:
+ print(
+ f"Package {package_name} can be updated from version {installed_version} to {latest_version}."
)
+ else:
+ print(f"Package {package_name} is up to date.")
- @complex_handle_errors(loggering=logging)
- def in_search_(self, button):
- "Mencari file atau folder berdasarkan input pencarian, membuka file jika ditemukan, atau memperbarui daftar file jika folder ditemukan."
- search_query = self.search_edit.get_edit_text().replace("\\", "/").strip()
- if search_query:
- if ":" in search_query and not search_query.startswith("@[select]"):
- if os.path.isfile(search_query):
- dirname, file_name = os.path.dirname(
- search_query
- ), os.path.basename(search_query)
- try:
- with open(search_query, "r+", encoding="utf-8") as f:
- content = f.read()
- except UnicodeDecodeError:
- with open(search_query, "r+", encoding="latin-1") as f:
- content = f.read()
- content = content.replace("\t", " " * 4)
-
- self.undo_stack.append(content)
- self.text_editor.set_edit_text(content)
- self.current_file_name = file_name # Track the current file name
- self.main_layout.body.contents[2][0].set_title(file_name)
-
- elif os.path.isdir(search_query):
- dirname = search_query
- else:
- x, _y = os.path.split(search_query)
- if self.current_path.replace("\\", "/") == x.replace(
- "\\", "/"
- ) and os.path.isdir(x):
- search_query = str(create_file_or_folder(search_query))
- self.update_ui()
- self.file_list[:] = self.get_file_list()
-
- dirname = None
-
- if dirname:
- self.current_path = dirname
- self.file_list[:] = self.get_file_list()
- self.status_msg_footer_text.set_text(
- f"Search results for '{search_query}'"
- )
- else:
- search_resultsFile = [
- f for f in os.listdir(self.current_path) if search_query in f
- ]
- search_resultsModule = [
- module
- for module in self.module_package_Python.curents
- if search_query in module
- ]
- search_resultsHighlight_Text = self.highlight_text(search_query)
-
- if search_resultsFile and search_resultsModule:
- self.listmodules_from_package_Python[:] = self.create_modules_menus(
- search_resultsModule
- )
- self.file_list[:] = self.create_file_list(search_resultsFile)
- self.status_msg_footer_text.set_text(
- f"Search results for '{search_query}'"
- )
- elif search_resultsFile:
- self.file_list[:] = self.create_file_list(search_resultsFile)
- self.status_msg_footer_text.set_text(
- f"Search results for '{search_query}'"
- )
- else:
- if search_resultsModule:
- self.listmodules_from_package_Python[
- :
- ] = self.create_modules_menus(search_resultsModule)
- self.file_list[:] = self.get_file_list()
- self.status_msg_footer_text.set_text(
- f"Search results for '{search_query}'"
- )
- elif search_resultsHighlight_Text and not search_query.startswith(
- "@[files]"
- ):
- self.status_msg_footer_text.set_text(
- f"Search results for '{search_query}' {search_resultsHighlight_Text}"
- )
- else:
- if (
- search_query.startswith("@[select]")
- and search_query.find("[@rename]") > -1
- ):
- x = search_query.replace("@[select]", "", 1).split(
- "[@rename]", 1
- )
- if x.__len__() == 2:
- getREName = [
- f
- for f in os.listdir(self.current_path)
- if x[0] in f
- ]
- if getREName.__len__() > 0:
- oldfilesorfolder, newplace = [
- os.path.join(self.current_path, getREName[0]),
- os.path.join(self.current_path, x[1]),
- ]
- try:
- os.rename(oldfilesorfolder, newplace)
- self.status_msg_footer_text.set_text(
- f"Rename {getREName[0]} success"
- )
- self.update_ui()
- self.file_list[:] = self.get_file_list()
- except:
- pass
- else:
- self.status_msg_footer_text.set_text(
- f"Search results for {search_query}"
- )
-
- else:
- self.file_list[:] = self.get_file_list()
- self.listmodules_from_package_Python[:] = self.create_modules_menus(
- self.module_package_Python.curents
- )
- self.status_msg_footer_text.set_text("")
-
- @complex_handle_errors(loggering=logging)
- def create_file_list(self, files):
- "Membuat daftar widget untuk file yang ditemukan sesuai hasil pencarian."
- widgets = []
- for f in files:
- if os.path.isdir(os.path.join(self.current_path, f)):
- f = f + "/"
- button = PlainButton(f)
- urwid.connect_signal(button, "click", self.open_file, f)
- widgets.append(urwid.AttrMap(button, None, focus_map="reversed"))
- return widgets
-
- def system_usage(self):
- "Memantau penggunaan CPU dan menampilkan peringatan jika konsumsi CPU tinggi."
- timemming = timeout_v1()
- if timemming > 0.87:
- self.status_msg_footer_text.set_text("High CPU utilization alert")
-
- def update_ui(self):
- "Memperbarui tampilan UI aplikasi."
- self.loop.draw_screen()
-
- def update_uiV2(self, *args, **kwargs):
- "Memperbarui tampilan UI aplikasi."
- self.loop.set_alarm_in(timeout_v2(), self.update_uiV2)
- self.loop.draw_screen()
-
- def quit_app(self, button=None):
- "Menghentikan aplikasi dan menghapus alarm sistem jika ada."
- if self.system_alarm != None:
- self.loop.remove_alarm(self.system_alarm) # Hentikan alarm
- self.system_alarm = None
- raise urwid.ExitMainLoop()
-
- def run(self):
- "Memulai loop utama urwid untuk menjalankan aplikasi."
- self.loop.run()
-
-
-@complex_handle_errors(loggering=logging)
-def main(path: str):
- app = SuperNano(start_path=path)
- app.run()
-
-
-if __name__ == "__main__":
- set_low_priority(os.getpid())
- #########mendapatkan process terbaik tanpa membebani ram dan cpu
-
- safe_executor = SafeProcessExecutor(
- max_workers=2
- ) #########mendapatkan process terbaik tanpa membebani cpu
- safe_executor.submit(main, path=parse_args())
- time.sleep(timeout_v2())
- safe_executor.shutdown(
- wait=True
- ) ###mmenunggu process benar-benar berhenti tanpa memaksanya
- rd = StreamFile(
- file_path=fileloogiing,
- buffer_size=os.path.getsize(fileloogiing) + 2,
- print_delay=timeout_v2(),
- ) #########mendapatkan process terbaik membaca file logging tanpa membebani cpu
- for r in rd.readlines():
- print(r)
- rd.eraseFile() # membersihkan loggging
- rd.close()
From a136d8f8fdbac14e1ef69cb534f66c3b54b8acba Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 02:58:34 +0700
Subject: [PATCH 28/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/supernano.py b/supernano.py
index ad68349..1f0a3e9 100644
--- a/supernano.py
+++ b/supernano.py
@@ -439,7 +439,10 @@ def setup_main_menu(self):
timeout_v2() + 1,
lambda loop, user_data: self.system_usage(),
)
- urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
+ try:
+ urwid.TrustedLoop(self.loop).set_widget(self.main_layout)
+ except:
+ self.loop.widget = self.main_layout
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def create_modules_menus(self, listmodulename: list):
@@ -703,7 +706,7 @@ def open_file(self, button, file_name):
# if str(ext).lower() in ( ".pyx", ".pyz", ".py"):
# self.listmodules_from_package_Python[:] = self.modules_menus(self.current_path)
- self.main_layout.body.contents[1][0].set_title(file_name)
+ self.main_layout.body.contents[2][0].set_title(file_name)
else:
self.status_msg_footer_text.set_text("File access denied!")
@@ -812,7 +815,7 @@ def in_search_(self, button):
self.undo_stack.append(content)
self.text_editor.set_edit_text(content)
self.current_file_name = file_name # Track the current file name
- self.main_layout.body.contents[1][0].set_title(file_name)
+ self.main_layout.body.contents[2][0].set_title(file_name)
elif os.path.isdir(search_query):
dirname = search_query
From 1e93faf94518e7f3a06c4ad8ae5eb1e56974d21a Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 12:57:17 +0700
Subject: [PATCH 29/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 74 insertions(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 1f0a3e9..a5cb325 100644
--- a/supernano.py
+++ b/supernano.py
@@ -13,6 +13,8 @@
from libs.filemanager import (
StreamFile,
validate_file,
+ isvalidate_folder,
+ isvalidate_filename,
ModuleInspector,
create_file_or_folder,
resolve_relative_path,
@@ -30,6 +32,8 @@
from .filemanager import (
StreamFile,
validate_file,
+ isvalidate_folder,
+ isvalidate_filename,
ModuleInspector,
create_file_or_folder,
resolve_relative_path,
@@ -46,6 +50,8 @@
from filemanager import (
StreamFile,
validate_file,
+ isvalidate_folder,
+ isvalidate_filename,
ModuleInspector,
create_file_or_folder,
resolve_relative_path,
@@ -221,6 +227,18 @@ def paste_from_clipboard(self):
self.edit_pos = cursor_pos + len(self.clipboard)
+
+class SaveableEdit(urwid.Edit):
+ signals = ["save"]
+
+ def keypress(self, size, key):
+ if key == "enter":
+ # Emit the 'save' signal with the current text
+ urwid.emit_signal(self, "save", self.get_edit_text())
+ return True
+ return super().keypress(size, key)
+
+
class SuperNano:
"""
Kelas SuperNano yang sedang Anda kembangkan adalah text editor berbasis console yang menggunakan Python 3.6 ke atas dengan dukungan urwid[curses].
@@ -568,6 +586,13 @@ def handle_input(self, key):
descrip="Are you sure you Quit",
)
+ elif key in ("ctrl n", "ctrl N"):
+ self.show_popup(
+ menus=[*self.renameORcreatedPOP()],
+ title="Rename or Create",
+ descrip="AChoose to rename an existing item or create a new one in the current directory. Press ENter to done",
+ )
+
elif key in ("ctrl s", "ctrl S"):
# self.save_file()
self.show_popup(
@@ -608,7 +633,7 @@ def handle_input(self, key):
self.switch_to_secondary_layout()
elif key in ("f1", "ctrl e", "ctrl E"):
self.current_focus = 1 if self.current_focus == 0 else 0
-
+
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def get_current_edit(self):
"Mengembalikan widget edit yang sedang difokuskan (text editor atau search edit)."
@@ -622,6 +647,54 @@ def set_focus_on_click(self, widget, new_edit_text, index):
"Mengatur fokus pada widget edit berdasarkan klik dan indeks."
self.current_focus = index
+ @complex_handle_errors(loggering=logging, nomessagesNormal=False)
+ def renameORcreatedPOP(self):
+ select = urwid.Edit("Search or Create", "")
+ replaces = SaveableEdit("Replace ", "")
+
+ def on_save(button, *args):
+ slect = select.get_edit_text().strip()
+ if slect.__len__() <= 0:
+ return
+ getselect = [f for f in os.listdir(f"{self.current_path}") if slect in f]
+ if getselect and replaces.get_edit_text():
+ _y = replaces.get_edit_text().strip()
+ if isvalidate_folder(_y):
+ try:
+ selecfolder = resolve_relative_path(
+ self.current_path, getselect[0]
+ )
+ selecrepcae = resolve_relative_path(self.current_path, _y)
+ if os.path.isdir(selecfolder) or os.path.isfile(selecfolder):
+ os.rename(selecfolder, selecrepcae)
+ ms = str(f"Success renaming item")
+ except:
+ ms = str(f"Failed renaming item: {getselect[0]}")
+ else:
+ ms = str("Item to rename not found")
+ else:
+ x, _y = os.path.split(slect)
+ if os.path.isdir(x):
+ ms = str("Item to rename not found")
+ else:
+ if isvalidate_folder(_y) or _y.find(".") == -1:
+ ms = create_file_or_folder(
+ resolve_relative_path(self.current_path, slect)
+ )
+ elif isvalidate_filename(_y) or _y.find(".") > 0:
+ ms = create_file_or_folder(
+ resolve_relative_path(self.current_path, slect)
+ )
+ else:
+ ms = str("Item to rename not found")
+
+ self.switch_to_secondary_layout()
+ self.status_msg_footer_text.set_text(ms)
+
+ urwid.connect_signal(replaces, "save", on_save)
+ return [select, replaces]
+
+
@complex_handle_errors(loggering=logging, nomessagesNormal=False)
def copy_text_to_clipboard(self):
"Menyalin teks dari widget edit yang sedang aktif ke clipboard."
From 2a7ee642567d84b48776dcb185c73db301752318 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 12:59:18 +0700
Subject: [PATCH 30/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 96 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 96 insertions(+)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index 3c2117f..e3a6b91 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -312,6 +312,102 @@ def create_file_or_folder(path: str) -> str:
else:
return "Something happened."
+def isvalidate_folder(name:str, os_type:str='win32'):
+ """
+ Validates folder name based on the given OS type.
+
+ Args:
+ name (str): Name of the folder to validate.
+ os_type (str): Type of the OS ('windows', 'mac', or 'linux').
+
+ Returns:
+ bool: True if the name is valid, False otherwise.
+ """
+
+ # Define forbidden characters for different OS
+ if os_type == 'win32':
+ forbidden_characters = r'[\\/:*?"<>|]'
+ forbidden_endings = [' ', '.']
+ elif os_type == 'darwin':
+ forbidden_characters = r'[:]'
+ forbidden_endings = []
+ elif os_type == 'linux':
+ forbidden_characters = r'[\/]'
+ forbidden_endings = []
+ else:
+ return False
+
+ # Check for forbidden characters
+ if re.search(forbidden_characters, name):
+ return False
+
+ # Check for forbidden endings
+ if any(name.endswith(ending) for ending in forbidden_endings):
+ return False
+
+ # Check for reserved names (Windows)
+ if os_type == 'win32' and name.upper() in ('CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',
+ 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'):
+ return False
+
+ # Check for length restrictions (Windows: 260 characters max)
+ if os_type == 'win32' and len(name) > 260:
+ return False
+
+ # Check for trailing spaces in Linux/Unix/MacOS
+ if os_type in ['linux', 'darwin'] and name != name.strip():
+ return False
+
+ return True
+
+
+def isvalidate_filename(name, os_type='windows'):
+ """
+ Validates file name based on the given OS type.
+
+ Args:
+ name (str): Name of the file to validate.
+ os_type (str): Type of the OS ('windows', 'mac', or 'linux').
+
+ Returns:
+ bool: True if the name is valid, False otherwise.
+ """
+
+ # Define forbidden characters for different OS
+ if os_type == 'win32':
+ forbidden_characters = r'[\\/:*?"<>|]'
+ forbidden_endings = ['.']
+ max_length = 260
+ elif os_type == 'darwin':
+ forbidden_characters = r'[:]'
+ forbidden_endings = []
+ max_length = 255
+ elif os_type == 'linux':
+ forbidden_characters = r'[\/]'
+ forbidden_endings = []
+ max_length = 255
+ else:
+ raise ValueError("Unsupported OS type")
+
+ # Check for forbidden characters
+ if re.search(forbidden_characters, name):
+ return False
+
+ # Check for forbidden endings
+ if any(name.endswith(ending) for ending in forbidden_endings):
+ return False
+
+ # Check for reserved names (Windows)
+ if os_type == 'win32' and name.upper() in ('CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9',
+ 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9'):
+ return False
+
+ # Check for length restrictions
+ if len(name) > max_length:
+ return False
+
+ return True
+
def is_binary_file(file_path):
"""
From 0d04d8b3b8a0116b4030fc2815195bf7881b123b Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 13:08:33 +0700
Subject: [PATCH 31/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index a5cb325..52e0633 100644
--- a/supernano.py
+++ b/supernano.py
@@ -323,7 +323,7 @@ def create_button(module_name):
"Press ctrl + q to exit, Arrow keys to navigate"
)
self.main_footer_text = urwid.Text(
- "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+R : Refresh UI ESC: Quit "
+ "Ctrl+S : Save file Ctrl+D : Delete File Ctrl+Z : Undo Edit Ctrl+Y : Redo Edit Ctrl+E : Redirect input Ctrl+N : Rename/Create Ctrl+R : Refresh UI ESC: Quit "
)
# Event loop
From 14335c177b3a320a89ba97b94c9de3e31f101790 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 13:34:35 +0700
Subject: [PATCH 32/40] filemanager.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
libs/filemanager.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libs/filemanager.py b/libs/filemanager.py
index e3a6b91..2b1fa8f 100644
--- a/libs/filemanager.py
+++ b/libs/filemanager.py
@@ -1,4 +1,4 @@
-import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading
+import os, sys, time, shutil, psutil, inspect, importlib, pkg_resources, pkgutil, json, logging, threading, re
try:
from .helperegex import (
From 35b79d95b782b8bb558e24947f83f4637bc92562 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 14:15:18 +0700
Subject: [PATCH 33/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 52e0633..6d6f0a1 100644
--- a/supernano.py
+++ b/supernano.py
@@ -782,7 +782,10 @@ def open_file(self, button, file_name):
self.main_layout.body.contents[2][0].set_title(file_name)
else:
- self.status_msg_footer_text.set_text("File access denied!")
+ if validate_folder(os.path.dirname(file_path)):
+ self.current_file_name = file_name # Track the current file name
+
+ self.status_msg_footer_text.set_text("File access denied!")
if str(ext).lower().startswith((".pyx", ".pyz", ".py")) != True:
self.Text_Deinspect_modules_from_package_Python.set_text(
From 039693150b7ce39c0bd37f867edca380534ab8d8 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 14:16:23 +0700
Subject: [PATCH 34/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/supernano.py b/supernano.py
index 6d6f0a1..e2f2200 100644
--- a/supernano.py
+++ b/supernano.py
@@ -782,10 +782,10 @@ def open_file(self, button, file_name):
self.main_layout.body.contents[2][0].set_title(file_name)
else:
- if validate_folder(os.path.dirname(file_path)):
- self.current_file_name = file_name # Track the current file name
+ if validate_folder(os.path.dirname(file_path)):
+ self.current_file_name = file_name # Track the current file name
- self.status_msg_footer_text.set_text("File access denied!")
+ self.status_msg_footer_text.set_text("File access denied!")
if str(ext).lower().startswith((".pyx", ".pyz", ".py")) != True:
self.Text_Deinspect_modules_from_package_Python.set_text(
From f23ee576bd50ffd54db461d9fcded1aab49ba293 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Mon, 2 Sep 2024 15:35:41 +0700
Subject: [PATCH 35/40] Update README.md
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index b10ddc4..55eae6a 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Here is the documentation for the `SuperNano` script, a powerful console-based t
## Key Features
- **Text Editing**: Text editor with multiline support, undo-redo, copy-paste, and file saving.
- **File Management**: Allows directory navigation, opening and saving files, and creating and deleting files.
-- Module Inspection**: Features for inspecting Python modules, displaying information about global variables, classes, and functions within the module.
+- **Module Inspection**: Features for inspecting Python modules, displaying information about global variables, classes, and functions within the module.
## Classes and Methods
From 49c4ed76f5ea433aca4f79b4a2e226db3a8cf78f Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 8 Sep 2024 10:53:24 +0700
Subject: [PATCH 36/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/supernano.py b/supernano.py
index e2f2200..3bd7af1 100644
--- a/supernano.py
+++ b/supernano.py
@@ -273,7 +273,7 @@ def __init__(self, start_path="."):
######Create widgets modulepython menu
def create_button(module_name):
button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, module_name)
+ urwid.connect_signal(button, "click", self.inspect_module, user_args=[module_name])
return urwid.AttrMap(button, None, focus_map="reversed")
self.listmodules_from_package_Python = urwid.SimpleFocusListWalker(
@@ -466,7 +466,7 @@ def setup_main_menu(self):
def create_modules_menus(self, listmodulename: list):
def create_button(module_name):
button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, module_name)
+ urwid.connect_signal(button, "click", self.inspect_module, user_args=[module_name])
return urwid.AttrMap(button, None, focus_map="reversed")
return [create_button(module) for module in listmodulename]
From 6892a071e3ae0cc5c8fc61ffbb2eed70b95107cb Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 8 Sep 2024 11:06:50 +0700
Subject: [PATCH 37/40] Add files via upload
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
__init__.py | 12 ++++++++++++
__main__.py | 8 ++++++++
2 files changed, 20 insertions(+)
create mode 100644 __init__.py
create mode 100644 __main__.py
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..8a75189
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,12 @@
+import os, sys
+if getattr(sys, 'frozen', False):
+ # Jika aplikasi telah dibundel sebagai .exe
+ __file__ = str(sys.executable)
+
+
+encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0xCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQoKICAgIHdlamlfaWJpZ3l4c3Yud3lmcW14KHFlbXIsIHRleGw9dGV2d2lfZXZrdygpKQoKICAgIHhtcWkud3BpaXQoeG1xaXN5eF96MigpKQoKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCgogICAgdmggPSBXeHZpZXFKbXBpKAogICAgICAgIGptcGlfdGV4bD1qbXBpcHNza21tcmssCiAgICAgICAgZnlqaml2X3dtZGk9c3cudGV4bC5raXh3bWRpKGptcGlwc3NrbW1yaykgKyAyLAogICAgICAgIHR2bXJ4X2hpcGVjPXhtcWlzeXhfejIoKSwKICAgICkgICMjIyMjIyMjI3FpcmhldGV4b2VyIHR2c2dpd3cgeGl2ZmVtbyBxaXFmZWdlIGptcGkgcHNra21yayB4ZXJ0ZSBxaXFmaWZlcm0gZ3R5CgogICAganN2IHYgbXIgdmgudmllaHBtcml3KCk6CiAgICAgICAgdHZtcngodikKCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKCiAgICB2aC5ncHN3aSgpCg=="
+encoded_dataNOW= "CnFlbXIodGV4bD10ZXZ3aV9ldmt3KCkpICMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKCiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQoKICAgIHZoLml2ZXdpSm1waSgpICAjIHFpcWZpdndtbG9lciBwc2tra21yawoKICAgIHZoLmdwc3dpKCkKICAgIA=="
+
+script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")
+all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir]
+sys.path.extend(all_system_paths)
diff --git a/__main__.py b/__main__.py
new file mode 100644
index 0000000..7b1ad3a
--- /dev/null
+++ b/__main__.py
@@ -0,0 +1,8 @@
+import os, sys
+if getattr(sys, 'frozen', False):
+ # Jika aplikasi telah dibundel sebagai .exe
+ __file__ = str(sys.executable)
+
+script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")
+all_system_paths = ["/".join(script_dir.split("/")[:-1]), script_dir]
+sys.path.extend(all_system_paths)
From 34df33348851ba7302ffa2d340fcf6b5a52cc921 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 8 Sep 2024 11:16:01 +0700
Subject: [PATCH 38/40] __init__.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/__init__.py b/__init__.py
index 8a75189..e5e2d1a 100644
--- a/__init__.py
+++ b/__init__.py
@@ -4,7 +4,7 @@
__file__ = str(sys.executable)
-encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0xCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQoKICAgIHdlamlfaWJpZ3l4c3Yud3lmcW14KHFlbXIsIHRleGw9dGV2d2lfZXZrdygpKQoKICAgIHhtcWkud3BpaXQoeG1xaXN5eF96MigpKQoKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCgogICAgdmggPSBXeHZpZXFKbXBpKAogICAgICAgIGptcGlfdGV4bD1qbXBpcHNza21tcmssCiAgICAgICAgZnlqaml2X3dtZGk9c3cudGV4bC5raXh3bWRpKGptcGlwc3NrbW1yaykgKyAyLAogICAgICAgIHR2bXJ4X2hpcGVjPXhtcWlzeXhfejIoKSwKICAgICkgICMjIyMjIyMjI3FpcmhldGV4b2VyIHR2c2dpd3cgeGl2ZmVtbyBxaXFmZWdlIGptcGkgcHNra21yayB4ZXJ0ZSBxaXFmaWZlcm0gZ3R5CgogICAganN2IHYgbXIgdmgudmllaHBtcml3KCk6CiAgICAgICAgdHZtcngodikKCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKCiAgICB2aC5ncHN3aSgpCg=="
+encoded_dataOLD= "CiAgICB3ZWppX2liaWd5eHN2ID0gV2VqaVR2c2dpd3dJYmlneXhzdigKICAgICAgICBxZWJfYXN2b2l2dz0yCiAgICApICAjIyMjIyMjIyNxaXJoZXRleG9lciB0dnNnaXd3IHhpdmZlbW8geGVydGUgcWlxZmlmZXJtIGd0eQogICAgd2VqaV9pYmlneXhzdi53eWZxbXgocWVtciwgdGV4bD10ZXZ3aV9ldmt3KCkpCiAgICB4bXFpLndwaWl0KHhtcWlzeXhfejIoKSkKICAgIHdlamlfaWJpZ3l4c3Yud2x5eGhzYXIoCiAgICAgICAgYWVteD1YdnlpCiAgICApICAjIyNxcWlyeXJra3kgdHZzZ2l3dyBmaXJldi1maXJldiBmaXZsaXJ4bSB4ZXJ0ZSBxaXFlb3dlcmNlCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKICAgIGpzdiB2IG1yIHZoLnZpZWhwbXJpdygpOgogICAgICAgIHR2bXJ4KHYpCiAgICB2aC5pdmV3aUptcGkoKSAgIyBxaXFmaXZ3bWxvZXIgcHNra2ttcmsKICAgIHZoLmdwc3dpKCk=="
encoded_dataNOW= "CnFlbXIodGV4bD10ZXZ3aV9ldmt3KCkpICMjI3FxaXJ5cmtreSB0dnNnaXd3IGZpcmV2LWZpcmV2IGZpdmxpcnhtIHhlcnRlIHFpcWVvd2VyY2UKCiAgICB2aCA9IFd4dmllcUptcGkoCiAgICAgICAgam1waV90ZXhsPWptcGlwc3NrbW1yaywKICAgICAgICBmeWpqaXZfd21kaT1zdy50ZXhsLmtpeHdtZGkoam1waXBzc2ttbXJrKSArIDIsCiAgICAgICAgdHZtcnhfaGlwZWM9eG1xaXN5eF96MigpLAogICAgKSAgIyMjIyMjIyMjcWlyaGV0ZXhvZXIgdHZzZ2l3dyB4aXZmZW1vIHFpcWZlZ2Ugam1waSBwc2trbXJrIHhlcnRlIHFpcWZpZmVybSBndHkKCiAgICBqc3YgdiBtciB2aC52aWVocG1yaXcoKToKICAgICAgICB0dm1yeCh2KQoKICAgIHZoLml2ZXdpSm1waSgpICAjIHFpcWZpdndtbG9lciBwc2tra21yawoKICAgIHZoLmdwc3dpKCkKICAgIA=="
script_dir = os.path.dirname(os.path.realpath(__file__)).replace("\\", "/")
From 9b8248395687887379233382ac4c9ad549e24183 Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 8 Sep 2024 13:45:18 +0700
Subject: [PATCH 39/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/supernano.py b/supernano.py
index 3bd7af1..afb447c 100644
--- a/supernano.py
+++ b/supernano.py
@@ -2,7 +2,8 @@
import pyperclip
import os, sys, shutil, logging, time, threading, argparse
from datetime import datetime
-
+if getattr(sys, 'frozen', False):
+ __file__ = sys.executable
try:
from libs.helperegex import findpositions
from libs.titlecommand import get_console_title, set_console_title
From fd5be68f7f4d330cee874a1ae386e3d36ad36e3a Mon Sep 17 00:00:00 2001
From: LcferShell <71859305+LcfherShell@users.noreply.github.com>
Date: Sun, 8 Sep 2024 13:58:35 +0700
Subject: [PATCH 40/40] supernano.py
Signed-off-by: LcferShell <71859305+LcfherShell@users.noreply.github.com>
---
supernano.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/supernano.py b/supernano.py
index afb447c..a85bb5d 100644
--- a/supernano.py
+++ b/supernano.py
@@ -274,7 +274,7 @@ def __init__(self, start_path="."):
######Create widgets modulepython menu
def create_button(module_name):
button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, user_args=[module_name])
+ urwid.connect_signal(button, "click", self.inspect_module, module_name)
return urwid.AttrMap(button, None, focus_map="reversed")
self.listmodules_from_package_Python = urwid.SimpleFocusListWalker(
@@ -467,7 +467,7 @@ def setup_main_menu(self):
def create_modules_menus(self, listmodulename: list):
def create_button(module_name):
button = PlainButton(module_name)
- urwid.connect_signal(button, "click", self.inspect_module, user_args=[module_name])
+ urwid.connect_signal(button, "click", self.inspect_module, module_name)
return urwid.AttrMap(button, None, focus_map="reversed")
return [create_button(module) for module in listmodulename]