1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
# SPDX-FileCopyrightText: © 2016 Germar Reitze
# SPDX-FileCopyrightText: © 2025 Christian Buhtz <c.buhtz@posteo.jp>
#
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This file is part of the program "Back In Time" which is released under GNU
# General Public License v2 (GPLv2). See LICENSES directory or go to
# <https://spdx.org/licenses/GPL-2.0-or-later.html>.
# File split from qttools.py.
"""Improved file dialog"""
from pathlib import Path
from PyQt6.QtGui import QShortcut
from PyQt6.QtCore import QDir
from PyQt6.QtWidgets import (QAbstractItemView,
QDialog,
QFileDialog,
QListView,
QTreeView,
QToolButton,
QWidget)
class FileDialog(QFileDialog):
"""Flexible non-native File dialog able to handle hidden files.
It is a non-native dialog. An extra toggle button for hidden files is added
including a shortcut Ctrl+H.
"""
# PyLint bug: https://github.com/pylint-dev/pylint/issues/8675
# pylint: disable-next=too-many-positional-arguments,too-many-arguments
def __init__(self, # noqa: PLR0913
parent: QWidget,
title: str,
show_hidden: bool = True,
allow_multiselection: bool = True,
dirs_only: bool = False,
start_dir: Path = None):
super().__init__(
parent=parent,
caption=title,
directory=str(start_dir) if start_dir else str(Path.cwd())
)
# Qt own dialog
self.setOption(QFileDialog.Option.DontUseNativeDialog, True)
self._add_button_show_hidden()
# Hidden files/dirs?
if not show_hidden:
self._slot_toggle_button_show_hidden()
# setup behavior: single/multiple dirs/files
if dirs_only:
# Directories
self.setOption(self.Option.ShowDirsOnly, dirs_only)
self.setFileMode(self.FileMode.Directory)
if allow_multiselection:
# Workaround for selecting multiple directories adopted from
# http://www.qtcentre.org/threads/
# 34226-QFileDialog-select-multiple-directories?
# p=158482#post158482
for cls in (QListView, QTreeView):
self.findChildren(cls)[0].setSelectionMode(
QAbstractItemView.SelectionMode.ExtendedSelection)
else:
self.setFileMode(
# Multiple files
self.FileMode.ExistingFiles if allow_multiselection
# Single files
else self.FileMode.ExistingFile
)
self._multiselect = allow_multiselection
def _add_button_show_hidden(self):
# pylint: disable-next=import-outside-toplevel
import icon # noqa: PLC0415
grid = self.layout() # dialogs main layout
hbox = grid.itemAt(1) # layout with the toolbar buttons
btn = QToolButton(self)
btn.setIcon(icon.SHOW_HIDDEN)
btn.setToolTip(_('Show/hide hidden files and directories (Ctrl+H)'))
btn.setCheckable(True)
hbox.insertWidget(3, btn)
btn.toggled.connect(self._slot_toggled_show_hidden)
shortcut = QShortcut('Ctrl+H', self)
shortcut.activated.connect(btn.toggle)
# Sync button and filter: Show hidden by default
self.setFilter(self.filter() | QDir.Filter.Hidden)
btn.setChecked(True)
def _slot_toggled_show_hidden(self, _enable: bool = None):
# toggle the filter
self.setFilter(self.filter() ^ QDir.Filter.Hidden)
def result(self) -> Path | list[Path] | None:
"""Show the dialog and return the result.
Returns:
One path or list of paths. ``None`` in case the dialog was
canceled.
"""
if self.exec() != QDialog.DialogCode.Accepted:
return [] if self._multiselect else None
if self._multiselect:
return [Path(fn) for fn in self.selectedFiles()]
return Path(self.selectedFiles()[0])
|