forked from Boria138/PortProtonQt
feat(file explorer): added ThumbnailLoader class
Signed-off-by: Boris Yumankulov <boria138@altlinux.org>
This commit is contained in:
@@ -6,7 +6,7 @@ from PySide6.QtGui import QPixmap, QIcon
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QFormLayout, QHBoxLayout, QLabel, QVBoxLayout, QListWidget, QScrollArea, QWidget, QListWidgetItem, QSizePolicy, QApplication, QProgressBar
|
||||
)
|
||||
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer
|
||||
from PySide6.QtCore import Qt, QObject, Signal, QMimeDatabase, QTimer, QThreadPool, QRunnable, Slot
|
||||
from icoextract import IconExtractor, IconExtractorError
|
||||
from PIL import Image
|
||||
from portprotonqt.config_utils import get_portproton_location, read_favorite_folders, read_theme_from_config
|
||||
@@ -179,9 +179,11 @@ class FileExplorer(QDialog):
|
||||
self.mime_db = QMimeDatabase() # Initialize QMimeDatabase for mimetype detection
|
||||
self.path_history = {} # Dictionary to store last selected item per directory
|
||||
self.initial_path = initial_path # Store initial path if provided
|
||||
self.thumbnail_cache = {} # Cache for loaded thumbnails
|
||||
self.pending_thumbnails = set() # Track files pending thumbnail loading
|
||||
self.setup_ui()
|
||||
|
||||
# Настройки окна
|
||||
# Window settings
|
||||
self.setWindowModality(Qt.WindowModality.ApplicationModal)
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
|
||||
|
||||
@@ -208,8 +210,115 @@ class FileExplorer(QDialog):
|
||||
self.current_path = os.path.expanduser("~") # Fallback to home if initial path is invalid
|
||||
self.update_file_list()
|
||||
|
||||
class ThumbnailLoader(QRunnable):
|
||||
"""Class for asynchronous thumbnail loading in a separate thread."""
|
||||
class Signals(QObject):
|
||||
thumbnail_ready = Signal(str, QIcon) # Signal for ready thumbnail: file path and icon
|
||||
|
||||
def __init__(self, file_path, mime_type, size=64):
|
||||
super().__init__()
|
||||
self.file_path = file_path
|
||||
self.mime_type = mime_type
|
||||
self.size = size
|
||||
self.signals = self.Signals()
|
||||
|
||||
@Slot()
|
||||
def run(self):
|
||||
"""Performs thumbnail loading in a background thread."""
|
||||
try:
|
||||
if self.mime_type.startswith("image/"):
|
||||
pixmap = QPixmap(self.file_path)
|
||||
if not pixmap.isNull():
|
||||
scaled_pixmap = pixmap.scaled(self.size, self.size, Qt.AspectRatioMode.KeepAspectRatio)
|
||||
self.signals.thumbnail_ready.emit(self.file_path, QIcon(scaled_pixmap))
|
||||
else:
|
||||
logger.warning("Failed to load image: %s", self.file_path)
|
||||
elif self.file_path.lower().endswith(".exe"):
|
||||
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
||||
if generate_thumbnail(self.file_path, tmp.name, size=self.size):
|
||||
pixmap = QPixmap(tmp.name)
|
||||
if not pixmap.isNull():
|
||||
self.signals.thumbnail_ready.emit(self.file_path, QIcon(pixmap))
|
||||
os.unlink(tmp.name)
|
||||
else:
|
||||
logger.warning("Failed to generate thumbnail for .exe: %s", self.file_path)
|
||||
except Exception as e:
|
||||
logger.error("Error loading thumbnail for %s: %s", self.file_path, str(e))
|
||||
|
||||
|
||||
def async_load_thumbnails(self, files, mime_db):
|
||||
"""
|
||||
Asynchronously loads thumbnails for a list of files.
|
||||
|
||||
Args:
|
||||
files (list): List of file names to process.
|
||||
mime_db (QMimeDatabase): QMimeDatabase instance for file type detection.
|
||||
"""
|
||||
thread_pool = QThreadPool.globalInstance()
|
||||
thread_pool.setMaxThreadCount(4) # Limit the number of threads
|
||||
|
||||
for f in files:
|
||||
file_path = os.path.join(self.current_path, f)
|
||||
if file_path in self.thumbnail_cache or file_path in self.pending_thumbnails:
|
||||
continue # Skip if already cached or pending
|
||||
mime_type = mime_db.mimeTypeForFile(file_path).name()
|
||||
if mime_type.startswith("image/") or file_path.lower().endswith(".exe"):
|
||||
self.pending_thumbnails.add(file_path)
|
||||
loader = self.ThumbnailLoader(file_path, mime_type, size=64)
|
||||
loader.signals.thumbnail_ready.connect(self.update_thumbnail)
|
||||
thread_pool.start(loader)
|
||||
|
||||
|
||||
@Slot(str, QIcon)
|
||||
def update_thumbnail(self, file_path, icon):
|
||||
"""
|
||||
Updates the icon for a file list item after thumbnail loading.
|
||||
|
||||
Args:
|
||||
file_path (str): Path to the file for which the thumbnail was loaded.
|
||||
icon (QIcon): Loaded icon.
|
||||
"""
|
||||
try:
|
||||
# Cache the thumbnail
|
||||
self.thumbnail_cache[file_path] = icon
|
||||
self.pending_thumbnails.discard(file_path)
|
||||
# Update the item in the file list
|
||||
file_name = os.path.basename(file_path)
|
||||
for i in range(self.file_list.count()):
|
||||
item = self.file_list.item(i)
|
||||
if item.text() == file_name:
|
||||
item.setIcon(icon)
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error("Error updating thumbnail for %s: %s", file_path, str(e))
|
||||
|
||||
|
||||
def load_visible_thumbnails(self):
|
||||
"""Load thumbnails only for visible items in the file list."""
|
||||
try:
|
||||
visible_range = self.file_list.count()
|
||||
first_visible = max(0, self.file_list.indexAt(self.file_list.viewport().rect().topLeft()).row())
|
||||
last_visible = min(visible_range - 1, self.file_list.indexAt(self.file_list.viewport().rect().bottomRight()).row() + 5)
|
||||
|
||||
files_to_load = []
|
||||
for i in range(first_visible, last_visible + 1):
|
||||
item = self.file_list.item(i)
|
||||
if not item:
|
||||
continue
|
||||
file_name = item.text()
|
||||
if file_name.endswith("/"):
|
||||
continue # Skip directories
|
||||
file_path = os.path.join(self.current_path, file_name)
|
||||
if file_path not in self.thumbnail_cache and file_path not in self.pending_thumbnails:
|
||||
files_to_load.append(file_name)
|
||||
|
||||
if files_to_load:
|
||||
self.async_load_thumbnails(files_to_load, self.mime_db)
|
||||
except Exception as e:
|
||||
logger.error("Error loading visible thumbnails: %s", str(e))
|
||||
|
||||
def get_mounted_drives(self):
|
||||
"""Получение списка смонтированных дисков из /proc/mounts, исключая системные пути"""
|
||||
"""Retrieve a list of mounted drives from /proc/mounts, excluding system paths."""
|
||||
mounted_drives = []
|
||||
try:
|
||||
with open('/proc/mounts') as f:
|
||||
@@ -218,20 +327,20 @@ class FileExplorer(QDialog):
|
||||
if len(parts) < 2:
|
||||
continue
|
||||
mount_point = parts[1]
|
||||
# Исключаем системные и временные пути, но сохраняем /run/media
|
||||
# Exclude system and temporary paths, but keep /run/media
|
||||
if (mount_point.startswith(('/dev', '/sys', '/proc', '/tmp', '/snap', '/var/lib')) or
|
||||
(mount_point.startswith('/run') and not mount_point.startswith('/run/media'))):
|
||||
continue
|
||||
# Проверяем, является ли точка монтирования директорией и доступна ли она
|
||||
# Check if the mount point is a directory and accessible
|
||||
if os.path.isdir(mount_point) and os.access(mount_point, os.R_OK):
|
||||
mounted_drives.append(mount_point)
|
||||
return sorted(mounted_drives)
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка при получении смонтированных дисков: {e}")
|
||||
logger.error(_("Error retrieving mounted drives: %s"), str(e))
|
||||
return []
|
||||
|
||||
def setup_ui(self):
|
||||
"""Настройка интерфейса"""
|
||||
"""Set up the user interface."""
|
||||
self.setWindowTitle(_("File Explorer"))
|
||||
self.setGeometry(100, 100, 600, 600)
|
||||
|
||||
@@ -240,7 +349,7 @@ class FileExplorer(QDialog):
|
||||
self.main_layout.setSpacing(10)
|
||||
self.setLayout(self.main_layout)
|
||||
|
||||
# Панель для смонтированных дисков и избранных папок
|
||||
# Panel for mounted drives and favorite folders
|
||||
self.drives_layout = QHBoxLayout()
|
||||
self.drives_scroll = QScrollArea()
|
||||
self.drives_scroll.setWidgetResizable(True)
|
||||
@@ -253,12 +362,12 @@ class FileExplorer(QDialog):
|
||||
self.drives_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
self.drives_scroll.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
|
||||
|
||||
# Путь
|
||||
# Path label
|
||||
self.path_label = QLabel()
|
||||
self.path_label.setStyleSheet(self.theme.FILE_EXPLORER_PATH_LABEL_STYLE)
|
||||
self.main_layout.addWidget(self.path_label)
|
||||
|
||||
# Список файлов
|
||||
# File list
|
||||
self.file_list = QListWidget()
|
||||
self.file_list.setStyleSheet(self.theme.FILE_EXPLORER_STYLE)
|
||||
self.file_list.itemClicked.connect(self.handle_item_click)
|
||||
@@ -267,7 +376,10 @@ class FileExplorer(QDialog):
|
||||
self.file_list.customContextMenuRequested.connect(self.show_folder_context_menu)
|
||||
self.main_layout.addWidget(self.file_list)
|
||||
|
||||
# Кнопки
|
||||
# Connect scroll signal for lazy loading
|
||||
self.file_list.verticalScrollBar().valueChanged.connect(self.load_visible_thumbnails)
|
||||
|
||||
# Buttons
|
||||
self.button_layout = QHBoxLayout()
|
||||
self.button_layout.setSpacing(10)
|
||||
self.select_button = AutoSizeButton(_("Select"), icon=theme_manager.get_icon("apply"))
|
||||
@@ -286,43 +398,43 @@ class FileExplorer(QDialog):
|
||||
if self.context_menu_manager:
|
||||
self.context_menu_manager.show_folder_context_menu(self, pos)
|
||||
else:
|
||||
logger.warning("ContextMenuManager not found in parent")
|
||||
logger.warning(_("ContextMenuManager not found in parent"))
|
||||
|
||||
def move_selection(self, direction):
|
||||
"""Перемещение выбора по списку"""
|
||||
"""Move selection in the list."""
|
||||
current_row = self.file_list.currentRow()
|
||||
if direction < 0 and current_row > 0: # Вверх
|
||||
if direction < 0 and current_row > 0: # Up
|
||||
self.file_list.setCurrentRow(current_row - 1)
|
||||
elif direction > 0 and current_row < self.file_list.count() - 1: # Вниз
|
||||
elif direction > 0 and current_row < self.file_list.count() - 1: # Down
|
||||
self.file_list.setCurrentRow(current_row + 1)
|
||||
self.file_list.scrollToItem(self.file_list.currentItem())
|
||||
|
||||
def handle_item_click(self, item):
|
||||
"""Обработка одинарного клика мышью"""
|
||||
"""Handle single mouse click."""
|
||||
try:
|
||||
self.file_list.setCurrentItem(item)
|
||||
self.path_history[self.current_path] = item.text() # Сохраняем выбранный элемент
|
||||
self.path_history[self.current_path] = item.text() # Save selected item
|
||||
logger.debug("Selected item: %s", item.text())
|
||||
except Exception as e:
|
||||
logger.error("Error in handle_item_click: %s", e)
|
||||
|
||||
def handle_item_double_click(self, item):
|
||||
"""Обработка двойного клика мышью по элементу списка"""
|
||||
"""Handle double mouse click on a list item."""
|
||||
try:
|
||||
self.file_list.setCurrentItem(item)
|
||||
self.path_history[self.current_path] = item.text() # Сохраняем выбранный элемент
|
||||
self.path_history[self.current_path] = item.text() # Save selected item
|
||||
selected = item.text()
|
||||
full_path = os.path.join(self.current_path, selected)
|
||||
if os.path.isdir(full_path):
|
||||
if selected == "../":
|
||||
# Переходим в родительскую директорию
|
||||
# Navigate to parent directory
|
||||
self.previous_dir()
|
||||
else:
|
||||
# Открываем директорию
|
||||
# Open directory
|
||||
self.current_path = os.path.normpath(full_path)
|
||||
self.update_file_list()
|
||||
elif not self.directory_only:
|
||||
# Выбираем файл, если directory_only=False
|
||||
# Select file if directory_only=False
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
else:
|
||||
@@ -331,7 +443,7 @@ class FileExplorer(QDialog):
|
||||
logger.error("Error in handle_item_double_click: %s", e)
|
||||
|
||||
def select_item(self):
|
||||
"""Обработка выбора файла/папки"""
|
||||
"""Handle file/folder selection."""
|
||||
if self.file_list.count() == 0:
|
||||
return
|
||||
|
||||
@@ -340,30 +452,30 @@ class FileExplorer(QDialog):
|
||||
|
||||
if os.path.isdir(full_path):
|
||||
if self.directory_only:
|
||||
# Подтверждаем выбор директории
|
||||
# Confirm directory selection
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
else:
|
||||
# Открываем директорию
|
||||
# Open directory
|
||||
self.current_path = os.path.normpath(full_path)
|
||||
self.update_file_list()
|
||||
else:
|
||||
if not self.directory_only:
|
||||
# Для файла отправляем нормализованный путь
|
||||
# Emit normalized path for file
|
||||
self.file_signal.file_selected.emit(os.path.normpath(full_path))
|
||||
self.accept()
|
||||
else:
|
||||
logger.debug("Selected item is not a directory, ignoring: %s", full_path)
|
||||
|
||||
def previous_dir(self):
|
||||
"""Возврат к родительской директории"""
|
||||
"""Navigate to parent directory."""
|
||||
try:
|
||||
if self.current_path == "/":
|
||||
return # Уже в корне
|
||||
return # Already at root
|
||||
|
||||
# Нормализуем путь (убираем конечный слеш, если есть)
|
||||
# Normalize path (remove trailing slash if present)
|
||||
normalized_path = os.path.normpath(self.current_path)
|
||||
# Получаем родительскую директорию
|
||||
# Get parent directory
|
||||
parent_dir = os.path.dirname(normalized_path)
|
||||
|
||||
if not parent_dir:
|
||||
@@ -389,7 +501,7 @@ class FileExplorer(QDialog):
|
||||
logger.error(f"Error ensuring button visible: {e}")
|
||||
|
||||
def update_drives_list(self):
|
||||
"""Обновление списка смонтированных дисков и избранных папок."""
|
||||
"""Update the list of mounted drives and favorite folders."""
|
||||
for i in reversed(range(self.drives_layout.count())):
|
||||
item = self.drives_layout.itemAt(i)
|
||||
if item and item.widget():
|
||||
@@ -401,7 +513,7 @@ class FileExplorer(QDialog):
|
||||
drives = self.get_mounted_drives()
|
||||
favorite_folders = read_favorite_folders()
|
||||
|
||||
# Добавляем смонтированные диски
|
||||
# Add mounted drives
|
||||
for drive in drives:
|
||||
drive_name = os.path.basename(drive) or drive.split('/')[-1] or drive
|
||||
button = AutoSizeButton(drive_name, icon=theme_manager.get_icon("mount_point"))
|
||||
@@ -411,7 +523,7 @@ class FileExplorer(QDialog):
|
||||
self.drives_layout.addWidget(button)
|
||||
self.drive_buttons.append(button)
|
||||
|
||||
# Добавляем избранные папки
|
||||
# Add favorite folders
|
||||
for folder in favorite_folders:
|
||||
folder_name = os.path.basename(folder) or folder.split('/')[-1] or folder
|
||||
button = AutoSizeButton(folder_name, icon=theme_manager.get_icon("folder"))
|
||||
@@ -421,92 +533,92 @@ class FileExplorer(QDialog):
|
||||
self.drives_layout.addWidget(button)
|
||||
self.drive_buttons.append(button)
|
||||
|
||||
# Добавляем растяжку, чтобы выровнять элементы
|
||||
# Add spacer to align elements
|
||||
spacer = QWidget()
|
||||
spacer.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
self.drives_layout.addWidget(spacer)
|
||||
|
||||
def select_drive(self):
|
||||
"""Обрабатывает выбор диска или избранной папки через геймпад."""
|
||||
"""Handle drive or favorite folder selection via gamepad."""
|
||||
focused_widget = QApplication.focusWidget()
|
||||
if isinstance(focused_widget, AutoSizeButton) and focused_widget in self.drive_buttons:
|
||||
drive_name = focused_widget.text().strip() # Удаляем пробелы
|
||||
logger.debug(f"Выбрано имя: {drive_name}")
|
||||
drive_name = focused_widget.text().strip() # Remove whitespace
|
||||
logger.debug(f"Selected name: {drive_name}")
|
||||
|
||||
# Специальная обработка корневого каталога
|
||||
# Special handling for root directory
|
||||
if drive_name == "/":
|
||||
if os.path.isdir("/") and os.access("/", os.R_OK):
|
||||
self.current_path = "/"
|
||||
self.update_file_list()
|
||||
logger.info("Выбран корневой каталог: /")
|
||||
logger.info("Selected root directory")
|
||||
return
|
||||
else:
|
||||
logger.warning("Корневой каталог недоступен: недостаточно прав или ошибка пути")
|
||||
logger.warning("Root directory is inaccessible: insufficient permissions or path error")
|
||||
return
|
||||
|
||||
# Проверяем избранные папки
|
||||
# Check favorite folders
|
||||
favorite_folders = read_favorite_folders()
|
||||
logger.debug(f"Избранные папки: {favorite_folders}")
|
||||
logger.debug(f"Favorite folders: {favorite_folders}")
|
||||
for folder in favorite_folders:
|
||||
folder_name = os.path.basename(os.path.normpath(folder)) or folder # Для корневых путей
|
||||
folder_name = os.path.basename(os.path.normpath(folder)) or folder # For root paths
|
||||
if folder_name == drive_name and os.path.isdir(folder) and os.access(folder, os.R_OK):
|
||||
self.current_path = os.path.normpath(folder)
|
||||
self.update_file_list()
|
||||
logger.info(f"Выбрана избранная папка: {self.current_path}")
|
||||
logger.info(f"Selected favorite folder: {self.current_path}")
|
||||
return
|
||||
|
||||
# Проверяем смонтированные диски
|
||||
# Check mounted drives
|
||||
mounted_drives = self.get_mounted_drives()
|
||||
logger.debug(f"Смонтированные диски: {mounted_drives}")
|
||||
logger.debug(f"Mounted drives: {mounted_drives}")
|
||||
for drive in mounted_drives:
|
||||
drive_basename = os.path.basename(os.path.normpath(drive)) or drive # Для корневых путей
|
||||
drive_basename = os.path.basename(os.path.normpath(drive)) or drive # For root paths
|
||||
if drive_basename == drive_name and os.path.isdir(drive) and os.access(drive, os.R_OK):
|
||||
self.current_path = os.path.normpath(drive)
|
||||
self.update_file_list()
|
||||
logger.info(f"Выбран смонтированный диск: {self.current_path}")
|
||||
logger.info(f"Selected mounted drive: {self.current_path}")
|
||||
return
|
||||
|
||||
logger.warning(f"Путь недоступен: {drive_name}.")
|
||||
logger.warning(f"Path is inaccessible: {drive_name}.")
|
||||
|
||||
def change_drive(self, drive_path):
|
||||
"""Переход к выбранному диску"""
|
||||
"""Navigate to the selected drive."""
|
||||
if os.path.isdir(drive_path) and os.access(drive_path, os.R_OK):
|
||||
self.current_path = os.path.normpath(drive_path)
|
||||
self.update_file_list()
|
||||
else:
|
||||
logger.warning(f"Путь диска недоступен: {drive_path}")
|
||||
logger.warning(f"Drive path is inaccessible: {drive_path}")
|
||||
|
||||
def update_file_list(self):
|
||||
"""Обновление списка файлов с превью в виде иконок"""
|
||||
"""Update the file list with asynchronous thumbnail loading."""
|
||||
self.file_list.clear()
|
||||
self.thumbnail_cache.clear() # Clear cache when changing directories
|
||||
self.pending_thumbnails.clear() # Clear pending thumbnails
|
||||
try:
|
||||
if self.current_path != "/":
|
||||
item = QListWidgetItem("../")
|
||||
folder_icon = theme_manager.get_icon("folder")
|
||||
# Ensure the icon is a QIcon
|
||||
if isinstance(folder_icon, str) and os.path.isfile(folder_icon):
|
||||
folder_icon = QIcon(folder_icon)
|
||||
elif not isinstance(folder_icon, QIcon):
|
||||
folder_icon = QIcon() # Fallback to empty icon
|
||||
folder_icon = QIcon()
|
||||
item.setIcon(folder_icon)
|
||||
self.file_list.addItem(item)
|
||||
|
||||
items = os.listdir(self.current_path)
|
||||
dirs = [d for d in items if os.path.isdir(os.path.join(self.current_path, d))]
|
||||
|
||||
# Добавляем директории
|
||||
# Add directories
|
||||
for d in sorted(dirs):
|
||||
item = QListWidgetItem(f"{d}/")
|
||||
folder_icon = theme_manager.get_icon("folder")
|
||||
# Ensure the icon is a QIcon
|
||||
if isinstance(folder_icon, str) and os.path.isfile(folder_icon):
|
||||
folder_icon = QIcon(folder_icon)
|
||||
elif not isinstance(folder_icon, QIcon):
|
||||
folder_icon = QIcon() # Fallback to empty icon
|
||||
folder_icon = QIcon()
|
||||
item.setIcon(folder_icon)
|
||||
self.file_list.addItem(item)
|
||||
|
||||
# Добавляем файлы только если directory_only=False
|
||||
# Add files only if directory_only=False
|
||||
if not self.directory_only:
|
||||
files = [f for f in items if os.path.isfile(os.path.join(self.current_path, f))]
|
||||
if self.file_filter:
|
||||
@@ -515,26 +627,14 @@ class FileExplorer(QDialog):
|
||||
elif isinstance(self.file_filter, tuple):
|
||||
files = [f for f in files if any(f.lower().endswith(ext) for ext in self.file_filter)]
|
||||
|
||||
# Add files to the list without immediate thumbnail loading
|
||||
for f in sorted(files):
|
||||
item = QListWidgetItem(f)
|
||||
file_path = os.path.join(self.current_path, f)
|
||||
mime_type = self.mime_db.mimeTypeForFile(file_path).name()
|
||||
|
||||
if mime_type.startswith("image/"):
|
||||
pixmap = QPixmap(file_path)
|
||||
if not pixmap.isNull():
|
||||
item.setIcon(QIcon(pixmap.scaled(64, 64, Qt.AspectRatioMode.KeepAspectRatio)))
|
||||
elif file_path.lower().endswith(".exe"):
|
||||
tmp = tempfile.NamedTemporaryFile(suffix='.png', delete=False)
|
||||
tmp.close()
|
||||
if generate_thumbnail(file_path, tmp.name, size=64):
|
||||
pixmap = QPixmap(tmp.name)
|
||||
if not pixmap.isNull():
|
||||
item.setIcon(QIcon(pixmap))
|
||||
os.unlink(tmp.name)
|
||||
|
||||
self.file_list.addItem(item)
|
||||
|
||||
# Load thumbnails for visible items only
|
||||
self.load_visible_thumbnails()
|
||||
|
||||
self.path_label.setText(_("Path: ") + self.current_path)
|
||||
|
||||
# Restore last selected item for this directory
|
||||
@@ -556,10 +656,10 @@ class FileExplorer(QDialog):
|
||||
self.file_list.setAlternatingRowColors(True)
|
||||
|
||||
except PermissionError:
|
||||
self.path_label.setText(f"Access denied: {self.current_path}")
|
||||
self.path_label.setText(_("Access denied: %s") % self.current_path)
|
||||
|
||||
def closeEvent(self, event):
|
||||
"""Закрытие окна"""
|
||||
"""Handle window closing."""
|
||||
try:
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_file_explorer_mode()
|
||||
@@ -573,13 +673,13 @@ class FileExplorer(QDialog):
|
||||
super().closeEvent(event)
|
||||
|
||||
def reject(self):
|
||||
"""Закрытие диалога"""
|
||||
"""Close the dialog."""
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_file_explorer_mode()
|
||||
super().reject()
|
||||
|
||||
def accept(self):
|
||||
"""Принятие диалога"""
|
||||
"""Accept the dialog."""
|
||||
if self.input_manager:
|
||||
self.input_manager.disable_file_explorer_mode()
|
||||
super().accept()
|
||||
|
Reference in New Issue
Block a user