Add formatter helper for file extensions filters (#1693)

This commit is contained in:
Veronica Berglyd Olsen
2024-02-11 21:08:08 +01:00
committed by GitHub
8 changed files with 53 additions and 30 deletions
+14 -1
View File
@@ -41,7 +41,7 @@ from PyQt5.QtCore import QCoreApplication, QUrl
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.error import logException from novelwriter.error import logException
from novelwriter.constants import nwConst, nwUnicode from novelwriter.constants import nwConst, nwLabels, nwUnicode, trConst
if TYPE_CHECKING: # pragma: no cover if TYPE_CHECKING: # pragma: no cover
from typing import TypeGuard # Requires Python 3.10 from typing import TypeGuard # Requires Python 3.10
@@ -248,6 +248,19 @@ def formatVersion(value: str) -> str:
return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ") return value.lower().replace("a", " Alpha ").replace("b", " Beta ").replace("rc", " RC ")
def formatFileFilter(extensions: list[str | tuple[str, str]]) -> str:
"""Format a list of extensions, or extension + label pairs into a
QFileDialog extensions filter.
"""
result = []
for ext in extensions:
if isinstance(ext, str):
result.append(f"{trConst(nwLabels.FILE_FILTERS.get(ext))} ({ext})")
elif isinstance(ext, tuple) and len(ext) == 2:
result.append(f"{ext[0]} ({ext[1]})")
return ";;".join(result)
## ##
# String Functions # String Functions
## ##
+6
View File
@@ -270,6 +270,12 @@ class nwLabels:
nwBuildFmt.J_HTML: ".json", nwBuildFmt.J_HTML: ".json",
nwBuildFmt.J_NWD: ".json", nwBuildFmt.J_NWD: ".json",
} }
FILE_FILTERS = {
"*.txt": QT_TRANSLATE_NOOP("Constant", "Text files"),
"*.md": QT_TRANSLATE_NOOP("Constant", "Markdown files"),
"*.nwd": QT_TRANSLATE_NOOP("Constant", "novelWriter files"),
"*": QT_TRANSLATE_NOOP("Constant", "All files"),
}
UNIT_NAME = { UNIT_NAME = {
"mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"), "mm": QT_TRANSLATE_NOOP("Constant", "Millimetres"),
"cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"), "cm": QT_TRANSLATE_NOOP("Constant", "Centimetres"),
+3 -5
View File
@@ -36,6 +36,7 @@ from PyQt5.QtWidgets import (
) )
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.common import formatFileFilter
from novelwriter.core.spellcheck import UserDictionary from novelwriter.core.spellcheck import UserDictionary
from novelwriter.extensions.configlayout import NColourLabel from novelwriter.extensions.configlayout import NColourLabel
@@ -184,12 +185,9 @@ class GuiWordList(QDialog):
SHARED.info(self.tr( SHARED.info(self.tr(
"Note: The import file must be a plain text file with UTF-8 or ASCII encoding." "Note: The import file must be a plain text file with UTF-8 or ASCII encoding."
)) ))
extFilter = [ ffilter = formatFileFilter(["*.txt", "*"])
"{0} (*.txt)".format(self.tr("Text files")),
"{0} (*)".format(self.tr("All files")),
]
path, _ = QFileDialog.getOpenFileName( path, _ = QFileDialog.getOpenFileName(
self, self.tr("Import File"), str(Path.home()), filter=";;".join(extFilter) self, self.tr("Import File"), str(Path.home()), filter=ffilter
) )
if path: if path:
try: try:
+3 -8
View File
@@ -63,7 +63,7 @@ from novelwriter.tools.writingstats import GuiWritingStats
from novelwriter.enum import ( from novelwriter.enum import (
nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView nwDocAction, nwDocInsert, nwDocMode, nwItemType, nwWidget, nwView
) )
from novelwriter.common import hexToInt from novelwriter.common import formatFileFilter, hexToInt
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -664,14 +664,9 @@ class GuiMain(QMainWindow):
return False return False
lastPath = CONFIG.lastPath() lastPath = CONFIG.lastPath()
extFilter = [ ffilter = formatFileFilter(["*.txt", "*.md", "*.nwd", "*"])
"{0} (*.txt)".format(self.tr("Text files")),
"{0} (*.md)".format(self.tr("Markdown files")),
"{0} (*.nwd)".format(self.tr("novelWriter files")),
"{0} (*)".format(self.tr("All files")),
]
loadFile, _ = QFileDialog.getOpenFileName( loadFile, _ = QFileDialog.getOpenFileName(
self, self.tr("Import File"), str(lastPath), filter=";;".join(extFilter) self, self.tr("Import File"), str(lastPath), filter=ffilter
) )
if not loadFile: if not loadFile:
return False return False
+4 -4
View File
@@ -32,6 +32,7 @@ from pathlib import Path
from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal from PyQt5.QtCore import QObject, QRunnable, QThreadPool, pyqtSignal
from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget from PyQt5.QtWidgets import QFileDialog, QMessageBox, QWidget
from novelwriter.common import formatFileFilter
from novelwriter.constants import nwFiles from novelwriter.constants import nwFiles
from novelwriter.core.spellcheck import NWSpellEnchant from novelwriter.core.spellcheck import NWSpellEnchant
@@ -221,13 +222,12 @@ class SharedData(QObject):
def getProjectPath(self, parent: QWidget, path: str | Path | None = None, def getProjectPath(self, parent: QWidget, path: str | Path | None = None,
allowZip: bool = False) -> Path | None: allowZip: bool = False) -> Path | None:
"""Open the file dialog and select a novelWriter project file.""" """Open the file dialog and select a novelWriter project file."""
label = (self.tr("novelWriter Project File or Zip") label = (self.tr("novelWriter Project File or Zip File")
if allowZip else self.tr("novelWriter Project File")) if allowZip else self.tr("novelWriter Project File"))
ext = f"{nwFiles.PROJ_FILE} *.zip" if allowZip else nwFiles.PROJ_FILE ext = f"{nwFiles.PROJ_FILE} *.zip" if allowZip else nwFiles.PROJ_FILE
ffilter = formatFileFilter([(label, ext), "*"])
selected, _ = QFileDialog.getOpenFileName( selected, _ = QFileDialog.getOpenFileName(
parent, self.tr("Open Project"), str(path or ""), filter=";;".join( parent, self.tr("Open Project"), str(path or ""), filter=ffilter
[f"{label} ({ext})", "{0} (*)".format(self.tr("All Files"))]
)
) )
return Path(selected) if selected else None return Path(selected) if selected else None
+5 -6
View File
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.error import formatException from novelwriter.error import formatException
from novelwriter.common import openExternalPath, formatInt, getFileSize from novelwriter.common import formatFileFilter, openExternalPath, formatInt, getFileSize
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -180,12 +180,11 @@ class GuiDictionaries(QDialog):
@pyqtSlot() @pyqtSlot()
def _doBrowseHunspell(self): def _doBrowseHunspell(self):
"""Browse for a Free/Libre Office dictionary.""" """Browse for a Free/Libre Office dictionary."""
extFilter = [ ffilter = formatFileFilter([
self.tr("Free or Libre Office extension ({0})").format("*.sox *.oxt"), (self.tr("Free or Libre Office extension"), "*.sox *.oxt"), "*"
self.tr("All files ({0})").format("*"), ])
]
soxFile, _ = QFileDialog.getOpenFileName( soxFile, _ = QFileDialog.getOpenFileName(
self, self.tr("Browse Files"), "", filter=";;".join(extFilter) self, self.tr("Browse Files"), "", filter=ffilter
) )
if soxFile: if soxFile:
path = Path(soxFile).absolute() path = Path(soxFile).absolute()
+1 -1
View File
@@ -381,7 +381,7 @@ class GuiWritingStats(QDialog):
# Generate the file name # Generate the file name
savePath = CONFIG.lastPath() / f"sessionStats.{fileExt}" savePath = CONFIG.lastPath() / f"sessionStats.{fileExt}"
savePath, _ = QFileDialog.getSaveFileName( savePath, _ = QFileDialog.getSaveFileName(
self, self.tr("Save Data As"), str(savePath), "%s (*.%s)" % (textFmt, fileExt) self, self.tr("Save Data As"), str(savePath), f"{textFmt} (*.{fileExt})"
) )
if not savePath: if not savePath:
return False return False
+17 -5
View File
@@ -34,11 +34,11 @@ from PyQt5.QtCore import QUrl
from novelwriter.common import ( from novelwriter.common import (
checkBool, checkFloat, checkInt, checkIntTuple, checkPath, checkString, checkBool, checkFloat, checkInt, checkIntTuple, checkPath, checkString,
checkStringNone, checkUuid, formatInt, formatTime, formatTimeStamp, checkStringNone, checkUuid, formatFileFilter, formatInt, formatTime,
formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle, isItemClass, formatTimeStamp, formatVersion, fuzzyTime, getFileSize, hexToInt, isHandle,
isItemLayout, isItemType, isTitleTag, jsonEncode, makeFileNameSafe, minmax, isItemClass, isItemLayout, isItemType, isTitleTag, jsonEncode,
numberToRoman, NWConfigParser, openExternalPath, readTextFile, simplified, makeFileNameSafe, minmax, numberToRoman, NWConfigParser, openExternalPath,
transferCase, xmlIndent, yesNo readTextFile, simplified, transferCase, xmlIndent, yesNo
) )
@@ -346,6 +346,18 @@ def testBaseCommon_formatVersion():
# END Test testBaseCommon_formatVersion # END Test testBaseCommon_formatVersion
@pytest.mark.base
def testBaseCommon_formatFileFilter():
"""Test the formatFileFilter function."""
assert formatFileFilter(["*.txt"]) == "Text files (*.txt)"
assert formatFileFilter(["*.txt", "*"]) == "Text files (*.txt);;All files (*)"
assert formatFileFilter([("Stuff", "*.stuff"), "*.txt", "*"]) == (
"Stuff (*.stuff);;Text files (*.txt);;All files (*)"
)
# END Test testBaseCommon_formatFileFilter
@pytest.mark.base @pytest.mark.base
def testBaseCommon_simplified(): def testBaseCommon_simplified():
"""Test the simplified function.""" """Test the simplified function."""