Add formatter helper for file extensions filters (#1693)
This commit is contained in:
+14
-1
@@ -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
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -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"),
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."""
|
||||||
|
|||||||
Reference in New Issue
Block a user