Release 2.7 Alpha 2 (#2198)
This commit is contained in:
+37
-27
@@ -42,14 +42,14 @@ if TYPE_CHECKING: # pragma: no cover
|
|||||||
# ============
|
# ============
|
||||||
|
|
||||||
__package__ = "novelwriter"
|
__package__ = "novelwriter"
|
||||||
__copyright__ = "Copyright 2018–2024, Veronica Berglyd Olsen"
|
__copyright__ = "Copyright 2018-2025 Veronica Berglyd Olsen"
|
||||||
__license__ = "GPLv3"
|
__license__ = "GPLv3"
|
||||||
__author__ = "Veronica Berglyd Olsen"
|
__author__ = "Veronica Berglyd Olsen"
|
||||||
__maintainer__ = "Veronica Berglyd Olsen"
|
__maintainer__ = "Veronica Berglyd Olsen"
|
||||||
__email__ = "code@vkbo.net"
|
__email__ = "code@vkbo.net"
|
||||||
__version__ = "2.7a1"
|
__version__ = "2.7a2"
|
||||||
__hexversion__ = "0x020700a1"
|
__hexversion__ = "0x020700a2"
|
||||||
__date__ = "2025-01-15"
|
__date__ = "2025-01-20"
|
||||||
__status__ = "Stable"
|
__status__ = "Stable"
|
||||||
__domain__ = "novelwriter.io"
|
__domain__ = "novelwriter.io"
|
||||||
|
|
||||||
@@ -65,20 +65,20 @@ CONFIG = Config()
|
|||||||
SHARED = SharedData()
|
SHARED = SharedData()
|
||||||
|
|
||||||
# ANSI Colours
|
# ANSI Colours
|
||||||
C_RED = "\033[91m"
|
RED = "\033[91m"
|
||||||
C_GREEN = "\033[92m"
|
GREEN = "\033[92m"
|
||||||
C_YELLOW = "\033[93m"
|
YELLOW = "\033[93m"
|
||||||
C_BLUE = "\033[94m"
|
BLUE = "\033[94m"
|
||||||
C_WHITE = "\033[97m"
|
WHITE = "\033[97m"
|
||||||
C_END = "\033[0m"
|
END = "\033[0m"
|
||||||
|
|
||||||
# Log Formats
|
# Log Format Components
|
||||||
L_TIME = "[{asctime:}]"
|
TIME = "[{asctime:}]"
|
||||||
L_FILE = "{filename:>18}"
|
FILE = "{filename:>18}"
|
||||||
L_LINE = "{lineno:<4d}"
|
LINE = "{lineno:<4d}"
|
||||||
L_LVLP = "{levelname:8}"
|
LVLP = "{levelname:8}"
|
||||||
L_LVLC = "{levelname:17}"
|
LVLC = "{levelname:17}"
|
||||||
L_TEXT = "{message:}"
|
TEXT = "{message:}"
|
||||||
|
|
||||||
|
|
||||||
def main(sysArgs: list | None = None) -> GuiMain | None:
|
def main(sysArgs: list | None = None) -> GuiMain | None:
|
||||||
@@ -104,11 +104,19 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
f"novelWriter {__version__} ({__date__})\n"
|
f"novelWriter {__version__} ({__date__})\n"
|
||||||
f"{__copyright__}\n"
|
f"{__copyright__}\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"This program is free software: you can redistribute it and/or modify\n"
|
||||||
|
"it under the terms of the GNU General Public License as published by\n"
|
||||||
|
"the Free Software Foundation, either version 3 of the License, or\n"
|
||||||
|
"(at your option) any later version.\n"
|
||||||
|
"\n"
|
||||||
"This program is distributed in the hope that it will be useful,\n"
|
"This program is distributed in the hope that it will be useful,\n"
|
||||||
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
|
||||||
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
|
||||||
"GNU General Public Licence for more details.\n"
|
"GNU General Public Licence for more details.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
"You should have received a copy of the GNU General Public License\n"
|
||||||
|
"along with this program. If not, see <https://www.gnu.org/licenses/>.\n"
|
||||||
|
"\n"
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
" -h, --help Print this message.\n"
|
" -h, --help Print this message.\n"
|
||||||
" -v, --version Print program version and exit.\n"
|
" -v, --version Print program version and exit.\n"
|
||||||
@@ -167,21 +175,21 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
if fmtFlags & 0b01:
|
if fmtFlags & 0b01:
|
||||||
# This will overwrite the default level names, and also ensure that
|
# This will overwrite the default level names, and also ensure that
|
||||||
# they can be converted back to integer levels
|
# they can be converted back to integer levels
|
||||||
logging.addLevelName(logging.DEBUG, f"{C_BLUE}DEBUG{C_END}")
|
logging.addLevelName(logging.DEBUG, f"{BLUE}DEBUG{END}")
|
||||||
logging.addLevelName(logging.INFO, f"{C_GREEN}INFO{C_END}")
|
logging.addLevelName(logging.INFO, f"{GREEN}INFO{END}")
|
||||||
logging.addLevelName(logging.WARNING, f"{C_YELLOW}WARNING{C_END}")
|
logging.addLevelName(logging.WARNING, f"{YELLOW}WARNING{END}")
|
||||||
logging.addLevelName(logging.ERROR, f"{C_RED}ERROR{C_END}")
|
logging.addLevelName(logging.ERROR, f"{RED}ERROR{END}")
|
||||||
logging.addLevelName(logging.CRITICAL, f"{C_RED}CRITICAL{C_END}")
|
logging.addLevelName(logging.CRITICAL, f"{RED}CRITICAL{END}")
|
||||||
|
|
||||||
# Determine Log Format
|
# Determine Log Format
|
||||||
if fmtFlags == 0b00:
|
if fmtFlags == 0b00:
|
||||||
logFmt = f"{L_LVLP} {L_TEXT}"
|
logFmt = f"{LVLP} {TEXT}"
|
||||||
elif fmtFlags == 0b01:
|
elif fmtFlags == 0b01:
|
||||||
logFmt = f"{L_LVLC} {L_TEXT}"
|
logFmt = f"{LVLC} {TEXT}"
|
||||||
elif fmtFlags == 0b10:
|
elif fmtFlags == 0b10:
|
||||||
logFmt = f"{L_TIME} {L_FILE}:{L_LINE} {L_LVLP} {L_TEXT}"
|
logFmt = f"{TIME} {FILE}:{LINE} {LVLP} {TEXT}"
|
||||||
elif fmtFlags == 0b11:
|
elif fmtFlags == 0b11:
|
||||||
logFmt = f"{L_TIME} {C_BLUE}{L_FILE}{C_END}:{C_WHITE}{L_LINE}{C_END} {L_LVLC} {L_TEXT}"
|
logFmt = f"{TIME} {BLUE}{FILE}{END}:{WHITE}{LINE}{END} {LVLC} {TEXT}"
|
||||||
|
|
||||||
# Setup Logging
|
# Setup Logging
|
||||||
pkgLogger = logging.getLogger(__package__)
|
pkgLogger = logging.getLogger(__package__)
|
||||||
@@ -272,7 +280,9 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
|
|
||||||
|
|
||||||
def _createApp(style: str) -> QApplication:
|
def _createApp(style: str) -> QApplication:
|
||||||
"""Create the app."""
|
"""Create the app. This is done in a function to make it easier to
|
||||||
|
block app creation during testing.
|
||||||
|
"""
|
||||||
app = QApplication([CONFIG.appName, (f"-style={style}")])
|
app = QApplication([CONFIG.appName, (f"-style={style}")])
|
||||||
app.setApplicationName(CONFIG.appName)
|
app.setApplicationName(CONFIG.appName)
|
||||||
app.setApplicationVersion(__version__)
|
app.setApplicationVersion(__version__)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.7a1" hexVersion="0x020700a1" fileVersion="1.5" fileRevision="4" timeStamp="2025-01-15 23:35:34">
|
<novelWriterXML appVersion="2.7a2" hexVersion="0x020700a2" fileVersion="1.5" fileRevision="4" timeStamp="2025-01-18 15:16:45">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2152" autoCount="281" editTime="95820">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2153" autoCount="281" editTime="95823">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
IDI_ICON1 ICON DISCARDABLE "novelwriter.ico"
|
IDI_ICON1 ICON DISCARDABLE "novelWriter.ico"
|
||||||
1 VERSIONINFO
|
1 VERSIONINFO
|
||||||
FILEVERSION 1,0,0,0
|
FILEVERSION 1,1,0,0
|
||||||
PRODUCTVERSION 1,0,0,0
|
PRODUCTVERSION 1,1,0,0
|
||||||
FILEOS 0x4
|
FILEOS 0x4
|
||||||
FILETYPE 0x1
|
FILETYPE 0x1
|
||||||
{
|
{
|
||||||
@@ -9,11 +9,11 @@ FILETYPE 0x1
|
|||||||
BLOCK "040904b0" {
|
BLOCK "040904b0" {
|
||||||
VALUE "CompanyName", "Veronica Berglyd Olsen\0"
|
VALUE "CompanyName", "Veronica Berglyd Olsen\0"
|
||||||
VALUE "FileDescription", "novelWriter Launcher\0"
|
VALUE "FileDescription", "novelWriter Launcher\0"
|
||||||
VALUE "FileVersion", "1.0.0.0\0"
|
VALUE "FileVersion", "1.1.0.0\0"
|
||||||
VALUE "LegalCopyright", "© 2025 Veronica Berglyd Olsen\0"
|
VALUE "LegalCopyright", "(c) 2025 Veronica Berglyd Olsen\0"
|
||||||
VALUE "OriginalFilename", "novelWriter.exe\0"
|
VALUE "OriginalFilename", "novelWriter.exe\0"
|
||||||
VALUE "ProductName", "novelWriter\0"
|
VALUE "ProductName", "novelWriter\0"
|
||||||
VALUE "ProductVersion", "1.0.0.0\0"
|
VALUE "ProductVersion", "1.1.0.0\0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BLOCK "VarFileInfo" {
|
BLOCK "VarFileInfo" {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
#include <tchar.h>
|
#include <tchar.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void _tmain( int argc, TCHAR *argv[] ) {
|
void _tmain(int argc, TCHAR *argv[]) {
|
||||||
STARTUPINFO si;
|
STARTUPINFO si;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
|
|
||||||
@@ -32,19 +32,20 @@ void _tmain( int argc, TCHAR *argv[] ) {
|
|||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
ZeroMemory(&pi, sizeof(pi));
|
ZeroMemory(&pi, sizeof(pi));
|
||||||
|
|
||||||
TCHAR fileName[MAX_PATH];
|
TCHAR path[MAX_PATH];
|
||||||
auto pathlen = GetModuleFileName(NULL, fileName, MAX_PATH);
|
auto pathlen = GetModuleFileName(NULL, path, MAX_PATH);
|
||||||
fileName[pathlen - 15] = 0;
|
path[pathlen - 15] = 0;
|
||||||
SetCurrentDirectory(fileName);
|
SetCurrentDirectory(path);
|
||||||
std::cout << "WorkDir: " << fileName << std::endl;
|
std::cout << "WorkDir: " << path << std::endl;
|
||||||
|
|
||||||
TCHAR cmd[MAX_PATH] = "pythonw.exe novelWriter.pyw";
|
TCHAR cmd[MAX_PATH] = TEXT("pythonw.exe novelWriter.pyw");
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
_tcscat_s(cmd, TEXT(" "));
|
_tcscat_s(cmd, TEXT(" "));
|
||||||
_tcscat_s(cmd, argv[1]);
|
_tcscat_s(cmd, argv[1]);
|
||||||
}
|
}
|
||||||
std::cout << "Command: " << cmd << std::endl;
|
std::cout << "Command: " << cmd << std::endl;
|
||||||
|
|
||||||
|
std::cout << "Launching novelWriter GUI" << std::endl;
|
||||||
CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
Binary file not shown.
@@ -30,8 +30,8 @@ import pytest
|
|||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
|
||||||
from novelwriter import (
|
from novelwriter import (
|
||||||
C_BLUE, C_END, C_WHITE, CONFIG, L_FILE, L_LINE, L_LVLC, L_LVLP, L_TEXT,
|
BLUE, CONFIG, END, FILE, LINE, LVLC, LVLP, TEXT, TIME, WHITE, _createApp,
|
||||||
L_TIME, _createApp, logger, main
|
logger, main
|
||||||
)
|
)
|
||||||
|
|
||||||
from tests.tools import clearLogHandlers
|
from tests.tools import clearLogHandlers
|
||||||
@@ -115,16 +115,14 @@ def testBaseInit_Options(monkeypatch, fncPath):
|
|||||||
main(["--info", "--color", f"--config={fncPath}", f"--data={fncPath}"])
|
main(["--info", "--color", f"--config={fncPath}", f"--data={fncPath}"])
|
||||||
assert ex.value.code == 0
|
assert ex.value.code == 0
|
||||||
assert logger.getEffectiveLevel() == logging.INFO
|
assert logger.getEffectiveLevel() == logging.INFO
|
||||||
assert getFormat() == f"{L_LVLC} {L_TEXT}"
|
assert getFormat() == f"{LVLC} {TEXT}"
|
||||||
|
|
||||||
clearLogHandlers()
|
clearLogHandlers()
|
||||||
with pytest.raises(SystemExit) as ex:
|
with pytest.raises(SystemExit) as ex:
|
||||||
main(["--debug", "--color", f"--config={fncPath}", f"--data={fncPath}"])
|
main(["--debug", "--color", f"--config={fncPath}", f"--data={fncPath}"])
|
||||||
assert ex.value.code == 0
|
assert ex.value.code == 0
|
||||||
assert logger.getEffectiveLevel() == logging.DEBUG
|
assert logger.getEffectiveLevel() == logging.DEBUG
|
||||||
assert getFormat() == (
|
assert getFormat() == f"{TIME} {BLUE}{FILE}{END}:{WHITE}{LINE}{END} {LVLC} {TEXT}"
|
||||||
f"{L_TIME} {C_BLUE}{L_FILE}{C_END}:{C_WHITE}{L_LINE}{C_END} {L_LVLC} {L_TEXT}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Log Levels wo/Color
|
# Log Levels wo/Color
|
||||||
clearLogHandlers()
|
clearLogHandlers()
|
||||||
@@ -132,14 +130,14 @@ def testBaseInit_Options(monkeypatch, fncPath):
|
|||||||
main(["--info", f"--config={fncPath}", f"--data={fncPath}"])
|
main(["--info", f"--config={fncPath}", f"--data={fncPath}"])
|
||||||
assert ex.value.code == 0
|
assert ex.value.code == 0
|
||||||
assert logger.getEffectiveLevel() == logging.INFO
|
assert logger.getEffectiveLevel() == logging.INFO
|
||||||
assert getFormat() == f"{L_LVLP} {L_TEXT}"
|
assert getFormat() == f"{LVLP} {TEXT}"
|
||||||
|
|
||||||
clearLogHandlers()
|
clearLogHandlers()
|
||||||
with pytest.raises(SystemExit) as ex:
|
with pytest.raises(SystemExit) as ex:
|
||||||
main(["--debug", f"--config={fncPath}", f"--data={fncPath}"])
|
main(["--debug", f"--config={fncPath}", f"--data={fncPath}"])
|
||||||
assert ex.value.code == 0
|
assert ex.value.code == 0
|
||||||
assert logger.getEffectiveLevel() == logging.DEBUG
|
assert logger.getEffectiveLevel() == logging.DEBUG
|
||||||
assert getFormat() == f"{L_TIME} {L_FILE}:{L_LINE} {L_LVLP} {L_TEXT}"
|
assert getFormat() == f"{TIME} {FILE}:{LINE} {LVLP} {TEXT}"
|
||||||
|
|
||||||
# Help and Version
|
# Help and Version
|
||||||
with pytest.raises(SystemExit) as ex:
|
with pytest.raises(SystemExit) as ex:
|
||||||
|
|||||||
Reference in New Issue
Block a user