From fdccb2ca4dfa26beeadf1c984c26a0d185ad1c17 Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 17:33:53 +0300
Subject: [PATCH 1/6] Add functions to check mounted devices and file locations
on USB; implement unmount confirmation dialog in gcodes.py
---
panels/gcodes.py | 159 ++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 150 insertions(+), 9 deletions(-)
diff --git a/panels/gcodes.py b/panels/gcodes.py
index c735e28dc..32f98f28d 100644
--- a/panels/gcodes.py
+++ b/panels/gcodes.py
@@ -1,5 +1,6 @@
import logging
import os
+import subprocess
import gi
@@ -20,6 +21,56 @@ def format_label(widget):
label.set_lines(3)
+def is_mounted_device(path):
+ """Проверяет, является ли путь примонтированным устройством"""
+ gcodes_base = "/home/pi/printer_data/gcodes"
+
+ # Убираем префикс 'gcodes/' если он есть
+ if path.startswith("gcodes/"):
+ path = path[7:]
+
+ # Если путь пустой, это не устройство
+ if not path or path == '':
+ return False
+
+ full_path = os.path.join(gcodes_base, path)
+
+ # Проверяем, что путь существует и является точкой монтирования
+ if os.path.exists(full_path) and os.path.ismount(full_path):
+ return True
+ return False
+
+
+def is_file_on_usb(filepath):
+ """Проверяет, находится ли файл на USB устройстве (флешке)"""
+ gcodes_base = "/home/pi/printer_data/gcodes"
+
+ # Убираем префикс 'gcodes/' если он есть
+ if filepath.startswith("gcodes/"):
+ filepath = filepath[7:]
+
+ # Получаем директорию файла
+ dir_path = os.path.dirname(filepath)
+
+ # Если файл в корне gcodes, он не на USB
+ if dir_path == '' or dir_path == '.':
+ return False
+
+ # Проверяем, является ли родительская директория примонтированным устройством
+ full_dir_path = os.path.join(gcodes_base, dir_path)
+
+ # Проверяем все родительские директории до gcodes
+ current_path = full_dir_path
+ while current_path != gcodes_base and current_path != os.path.dirname(gcodes_base):
+ if os.path.ismount(current_path):
+ return True
+ current_path = os.path.dirname(current_path)
+ if not current_path or current_path == '/':
+ break
+
+ return False
+
+
class Panel(ScreenPanel):
def __init__(self, screen, title):
title = title or (_("Print") if self._printer.extrudercount > 0 else _("Gcodes"))
@@ -156,9 +207,6 @@ def create_item(self, item):
rename = Gtk.Button(hexpand=False, vexpand=False, can_focus=False, always_show_image=True)
rename.get_style_context().add_class("color2")
rename.set_image(self._gtk.Image("files", self.list_button_size, self.list_button_size))
- move = Gtk.Button(hexpand=False, vexpand=False, can_focus=False, always_show_image=True)
- move.get_style_context().add_class("color3")
- move.set_image(self._gtk.Image("sd", self.list_button_size, self.list_button_size))
itemname = Gtk.Label(hexpand=True, halign=Gtk.Align.START, ellipsize=Pango.EllipsizeMode.END)
itemname.get_style_context().add_class("print-filename")
itemname.set_markup(f"{basename}")
@@ -169,14 +217,26 @@ def create_item(self, item):
row.attach(icon, 0, 0, 1, 2)
row.attach(itemname, 1, 0, 3, 1)
row.attach(info, 1, 1, 1, 1)
- row.attach(rename, 2, 1, 1, 1)
- row.attach(delete, 3, 1, 1, 1)
+
+ # Определяем позиции кнопок в зависимости от типа элемента
+ button_col = 2
if 'filename' in item:
icon.connect("clicked", self.confirm_print, path)
image_args = (path, icon, self.thumbsize / 2, True, "file")
delete.connect("clicked", self.confirm_delete_file, f"gcodes/{path}")
- move.connect("clicked", self.confirm_move_file, f"gcodes/{path}")
rename.connect("clicked", self.show_rename, f"gcodes/{path}")
+ # Показываем кнопку переноса только если файл находится на USB
+ if is_file_on_usb(f"gcodes/{path}"):
+ move = Gtk.Button(hexpand=False, vexpand=False, can_focus=False, always_show_image=True)
+ move.get_style_context().add_class("color3")
+ move.set_image(self._gtk.Image("sd", self.list_button_size, self.list_button_size))
+ move.connect("clicked", self.confirm_move_file, f"gcodes/{path}")
+ row.attach(move, button_col, 1, 1, 1)
+ button_col += 1
+ row.attach(rename, button_col, 1, 1, 1)
+ button_col += 1
+ row.attach(delete, button_col, 1, 1, 1)
+ button_col += 1
action_icon = "printer" if self._printer.extrudercount > 0 else "load"
action = self._gtk.Button(action_icon, style="color3")
action.connect("clicked", self.confirm_print, path)
@@ -184,21 +244,33 @@ def create_item(self, item):
action.set_vexpand(False)
action.set_halign(Gtk.Align.END)
if self._screen.width >= 400:
- row.attach(action, 4, 0, 1, 2)
+ row.attach(action, button_col, 0, 1, 2)
else:
icon.get_style_context().add_class("color3")
- row.attach(icon, 4, 0, 1, 2)
+ row.attach(icon, button_col, 0, 1, 2)
elif 'dirname' in item:
icon.connect("clicked", self.change_dir, path)
image_args = (None, icon, self.thumbsize / 2, True, "folder")
delete.connect("clicked", self.confirm_delete_directory, path)
rename.connect("clicked", self.show_rename, path)
+ # Проверяем, является ли папка примонтированным устройством
+ if is_mounted_device(path):
+ unmount = Gtk.Button(hexpand=False, vexpand=False, can_focus=False, always_show_image=True)
+ unmount.get_style_context().add_class("color4")
+ unmount.set_image(self._gtk.Image("sd", self.list_button_size, self.list_button_size))
+ unmount.connect("clicked", self.confirm_unmount, path)
+ row.attach(unmount, button_col, 1, 1, 1)
+ button_col += 1
+ row.attach(rename, button_col, 1, 1, 1)
+ button_col += 1
+ row.attach(delete, button_col, 1, 1, 1)
+ button_col += 1
action = self._gtk.Button("load", style="color3")
action.connect("clicked", self.change_dir, path)
action.set_hexpand(False)
action.set_vexpand(False)
action.set_halign(Gtk.Align.END)
- row.attach(action, 4, 0, 1, 2)
+ row.attach(action, button_col, 0, 1, 2)
else:
return
fbchild.add(row)
@@ -278,6 +350,75 @@ def confirm_delete_directory(self, widget, dirpath):
params
)
+ def confirm_unmount(self, widget, dirpath):
+ """Отмонтирует примонтированное устройство"""
+ logging.debug(f"Requesting unmount confirmation for {dirpath}")
+
+ if not is_mounted_device(dirpath):
+ logging.warning(f"Path {dirpath} is not a mounted device")
+ self._screen.show_popup_message(_("This directory is not a mounted device"))
+ return
+
+ # Используем диалог подтверждения
+ buttons = [
+ {"name": _("Unmount"), "response": Gtk.ResponseType.OK, "style": 'dialog-primary'},
+ {"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-secondary'}
+ ]
+
+ label = Gtk.Label(
+ hexpand=True, vexpand=True,
+ wrap=True, wrap_mode=Pango.WrapMode.WORD_CHAR,
+ ellipsize=Pango.EllipsizeMode.END
+ )
+ label.set_markup(_("Unmount device?") + f"\n\n{dirpath}")
+
+ self._gtk.Dialog(
+ _("Unmount Device"),
+ buttons,
+ label,
+ self.confirm_unmount_response,
+ dirpath
+ )
+
+ def confirm_unmount_response(self, dialog, response_id, dirpath):
+ """Обработчик ответа на диалог отмонтирования"""
+ self._gtk.remove_dialog(dialog)
+ if response_id != Gtk.ResponseType.OK:
+ return
+
+ logging.debug(f"Unmounting device {dirpath}")
+ gcodes_base = "/home/pi/printer_data/gcodes"
+
+ # Убираем префикс 'gcodes/' если он есть
+ clean_path = dirpath
+ if clean_path.startswith("gcodes/"):
+ clean_path = clean_path[7:]
+
+ full_path = os.path.join(gcodes_base, clean_path)
+
+ # Выполняем отмонтирование через subprocess
+ try:
+ result = subprocess.run(
+ ["sudo", "umount", full_path],
+ capture_output=True,
+ text=True,
+ timeout=10
+ )
+ if result.returncode == 0:
+ logging.info(f"Successfully unmounted {full_path}")
+ # Обновляем список файлов после отмонтирования
+ self._refresh_files()
+ self._screen.show_popup_message(_("Device unmounted successfully"))
+ else:
+ logging.error(f"Failed to unmount {full_path}: {result.stderr}")
+ self._screen.show_popup_message(_("Failed to unmount device") + f": {result.stderr}")
+ except subprocess.TimeoutExpired:
+ logging.error(f"Timeout while unmounting {full_path}")
+ self._screen.show_popup_message(_("Timeout while unmounting device"))
+ except Exception as e:
+ logging.error(f"Error unmounting {full_path}: {e}")
+ self._screen.show_popup_message(_("Error unmounting device") + f": {str(e)}")
+
def back(self):
if self.showing_rename:
self.hide_rename()
From 6c25a7ce4a2b880f7594736c8e4f414944f861e9 Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 17:42:55 +0300
Subject: [PATCH 2/6] Rename "Resave" button to "Move" in USB dialog; update
file existence check to use is_file_on_usb function for improved clarity and
functionality.
---
panels/gcodes.py | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/panels/gcodes.py b/panels/gcodes.py
index 32f98f28d..d2d353e06 100644
--- a/panels/gcodes.py
+++ b/panels/gcodes.py
@@ -497,7 +497,7 @@ def confirm_print(self, widget, filename):
buttons_usb = [
{"name": _("Delete"), "response": Gtk.ResponseType.REJECT, "style": 'dialog-error'},
- {"name": _("Resave"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-secondary'},
+ {"name": _("Move"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-secondary'},
{"name": action, "response": Gtk.ResponseType.OK, "style": 'dialog-primary'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-secondary'}
]
@@ -537,13 +537,12 @@ def confirm_print(self, widget, filename):
inside_box.pack_start(info_box, True, True, 0)
main_box.pack_start(inside_box, True, True, 0)
- # self._gtk.Dialog(f'{action} {filename}', buttons, main_box, self.confirm_print_response, filename)
-
- dir_path = "/home/pi/printer_data/gcodes/"
- if os.path.isfile(f"{dir_path} + {filename}"):
- self._gtk.Dialog(f'{action} {filename}', buttons, main_box, self.confirm_print_response, filename)
- else:
+
+ # Проверяем, находится ли файл на USB устройстве
+ if is_file_on_usb(f"gcodes/{filename}"):
self._gtk.Dialog(f'{action} {filename}', buttons_usb, main_box, self.confirm_print_response, filename)
+ else:
+ self._gtk.Dialog(f'{action} {filename}', buttons, main_box, self.confirm_print_response, filename)
def confirm_print_response(self, dialog, response_id, filename):
self._gtk.remove_dialog(dialog)
@@ -554,7 +553,7 @@ def confirm_print_response(self, dialog, response_id, filename):
self._screen._ws.klippy.print_start(filename)
elif response_id == Gtk.ResponseType.APPLY:
logging.info(f"Move file {filename} to internal storage")
- self.confirm_move_file(self, f"gcodes/{filename}")
+ self.confirm_move_file(None, f"gcodes/{filename}")
elif response_id == Gtk.ResponseType.REJECT:
self.confirm_delete_file(None, f"gcodes/{filename}")
From 6a2ff6e83f5b83ae73709ba7377882763958c219 Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 17:44:44 +0300
Subject: [PATCH 3/6] Add directory deletion after unmounting in gcodes.py;
rename "Move" button to "Resave" in USB dialog for clarity.
---
panels/gcodes.py | 16 +++++++++++++++-
1 file changed, 15 insertions(+), 1 deletion(-)
diff --git a/panels/gcodes.py b/panels/gcodes.py
index d2d353e06..48cd40af1 100644
--- a/panels/gcodes.py
+++ b/panels/gcodes.py
@@ -406,6 +406,20 @@ def confirm_unmount_response(self, dialog, response_id, dirpath):
)
if result.returncode == 0:
logging.info(f"Successfully unmounted {full_path}")
+ # Удаляем папку устройства после отмонтирования
+ try:
+ if os.path.exists(full_path):
+ # Используем API для удаления директории
+ params = {"path": clean_path, "force": True}
+ self._screen._send_action(
+ None,
+ "server.files.delete_directory",
+ params
+ )
+ logging.info(f"Deleted directory {full_path} after unmounting")
+ except Exception as e:
+ logging.error(f"Error deleting directory {full_path}: {e}")
+ # Продолжаем выполнение даже если удаление не удалось
# Обновляем список файлов после отмонтирования
self._refresh_files()
self._screen.show_popup_message(_("Device unmounted successfully"))
@@ -497,7 +511,7 @@ def confirm_print(self, widget, filename):
buttons_usb = [
{"name": _("Delete"), "response": Gtk.ResponseType.REJECT, "style": 'dialog-error'},
- {"name": _("Move"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-secondary'},
+ {"name": _("Resave"), "response": Gtk.ResponseType.APPLY, "style": 'dialog-secondary'},
{"name": action, "response": Gtk.ResponseType.OK, "style": 'dialog-primary'},
{"name": _("Cancel"), "response": Gtk.ResponseType.CANCEL, "style": 'dialog-secondary'}
]
From 27c3ffbf072cb035c78aa300b91d75668807d56f Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 17:48:56 +0300
Subject: [PATCH 4/6] Update popup message level for device unmount
confirmation in gcodes.py
---
panels/gcodes.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/panels/gcodes.py b/panels/gcodes.py
index 48cd40af1..6b31202a4 100644
--- a/panels/gcodes.py
+++ b/panels/gcodes.py
@@ -422,7 +422,7 @@ def confirm_unmount_response(self, dialog, response_id, dirpath):
# Продолжаем выполнение даже если удаление не удалось
# Обновляем список файлов после отмонтирования
self._refresh_files()
- self._screen.show_popup_message(_("Device unmounted successfully"))
+ self._screen.show_popup_message(_("Device unmounted successfully"), level=1)
else:
logging.error(f"Failed to unmount {full_path}: {result.stderr}")
self._screen.show_popup_message(_("Failed to unmount device") + f": {result.stderr}")
From e249ebc9ff22db9f9ef34b8ff01acdf2cd8884c4 Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 17:55:39 +0300
Subject: [PATCH 5/6] Refactor file transfer confirmation in gcodes.py to
clarify internal storage transfer; update Russian localization for improved
accuracy and new messages.
---
.../locales/ru/LC_MESSAGES/KlipperScreen.mo | Bin 28703 -> 29148 bytes
.../locales/ru/LC_MESSAGES/KlipperScreen.po | 16 +++++++++--
panels/gcodes.py | 26 +++++-------------
3 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.mo b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.mo
index f8531785d42178a26f2cc97d8a7265c158a0f967..956fd6003d35a80d1c046bd609eed194ab922a65 100644
GIT binary patch
delta 8350
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
diff --git a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
index 6a6527b53..99e4a7161 100644
--- a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
+++ b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: KlipperScreen\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-26 12:31-0300\n"
-"PO-Revision-Date: 2025-06-23 15:16+0300\n"
+"PO-Revision-Date: 2025-12-25 17:55+0300\n"
"Last-Translator: gfbdrgng \n"
"Language-Team: Russian \n"
"Language: ru\n"
@@ -448,7 +448,7 @@ msgid "Job Status"
msgstr "Статус работы"
msgid "Klipper Restart"
-msgstr "Klipper перезагружается"
+msgstr "Перезагрузить Klipper"
msgid "Klipper has disconnected"
msgstr "Нет связи с Klipper"
@@ -1135,3 +1135,15 @@ msgstr "После подтверждения принтер перейдет в
msgid "Emergency Homing"
msgstr "Аварийная парковка"
+
+msgid "After confirmation, the file will be transferred to internal storage."
+msgstr "После подтверждения файл будет перенесен на внутреннее хранилище."
+
+msgid "Transfer file?"
+msgstr "Перенести файл?"
+
+msgid "Unmount"
+msgstr "Отмонтировать"
+
+msgid "Unmount device?"
+msgstr "Отмонтировать устройство?"
diff --git a/panels/gcodes.py b/panels/gcodes.py
index 6b31202a4..353ea127e 100644
--- a/panels/gcodes.py
+++ b/panels/gcodes.py
@@ -320,25 +320,13 @@ def confirm_move_file(self, widget, filepath):
dest = "gcodes/" + filename
params = {"source": f"{filepath}",
"dest": f"{dest}"}
- gcodes_path = "/home/pi/printer_data/"
- check_file = os.path.exists(gcodes_path + dest)
- if check_file:
- self._screen._confirm_send_action(
- None,
- _("A file with this name already exists") + "\n\n" +
- _("You may be trying to move a file that is already in the main directory") +
- "\n\n" + _("Replace it?") + "\n\n" + filepath,
- "server.files.move",
- params
- )
- else:
- self._screen._confirm_send_action(
- None,
- _("This function is designed to move a file to the main directory") +
- "\n\n" + _("Move file to main directory?") + "\n\n" + filepath,
- "server.files.move",
- params
- )
+ self._screen._confirm_send_action(
+ None,
+ _("After confirmation, the file will be transferred to internal storage.") +
+ "\n\n" + _("Transfer file?") + "\n\n" + filepath,
+ "server.files.move",
+ params
+ )
def confirm_delete_directory(self, widget, dirpath):
logging.debug(f"Sending delete_directory {dirpath}")
From a1fbafb0e4b6a5711afb737b40bb424799e2d514 Mon Sep 17 00:00:00 2001
From: Vlad <427departament@gmail.com>
Date: Thu, 25 Dec 2025 18:00:40 +0300
Subject: [PATCH 6/6] Update Russian localization in KlipperScreen.po with new
unmounting messages and adjust PO-Revision-Date for accuracy.
---
.../locales/ru/LC_MESSAGES/KlipperScreen.mo | Bin 29148 -> 29624 bytes
.../locales/ru/LC_MESSAGES/KlipperScreen.po | 14 +++++++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.mo b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.mo
index 956fd6003d35a80d1c046bd609eed194ab922a65..ddcc9e6e6d1fe27c85b1bf9abc710a640604cdfe 100644
GIT binary patch
delta 8419
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
delta 8029
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
diff --git a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
index 99e4a7161..0c8b77ad9 100644
--- a/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
+++ b/ks_includes/locales/ru/LC_MESSAGES/KlipperScreen.po
@@ -7,7 +7,7 @@ msgstr ""
"Project-Id-Version: KlipperScreen\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-11-26 12:31-0300\n"
-"PO-Revision-Date: 2025-12-25 17:55+0300\n"
+"PO-Revision-Date: 2025-12-25 18:00+0300\n"
"Last-Translator: gfbdrgng \n"
"Language-Team: Russian \n"
"Language: ru\n"
@@ -1147,3 +1147,15 @@ msgstr "Отмонтировать"
msgid "Unmount device?"
msgstr "Отмонтировать устройство?"
+
+msgid "Device unmounted successfully"
+msgstr "Устройство успешно отмонтировано"
+
+msgid "Failed to unmount device"
+msgstr "Не удалось отмонтировать устройство"
+
+msgid "Timeout while unmounting device"
+msgstr "Истекло время ожидания при отмонтировании устройства"
+
+msgid "Error unmounting device"
+msgstr "Ошибка при отмонтировании устройства"