Files
novelWriter/novelwriter/__init__.py
T
2023-08-31 19:09:23 +02:00

256 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
novelWriter Init File
=======================
File History:
Created: 2018-09-22 [0.0.1]
This file is a part of novelWriter
Copyright 20182023, Veronica Berglyd Olsen
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import sys
import getopt
import logging
from PyQt5.QtWidgets import QApplication, QErrorMessage
from novelwriter.error import exceptionHandler, logException
from novelwriter.config import Config
from novelwriter.shared import SharedData
##
# Version Scheme
# ================
# Generally follows PEP 440
# Hex Version:
# - Digit 1,2 : Major Version (01-ff)
# - Digit 3,4 : Minor Version (01-ff)
# - Digit 5,6 : Patch Version (01-ff)
# - Digit 7 : Release Type (a: alpha, b: beta, c: candidate, f: final)
# - Digit 8 : Release Number (0-f)
#
# Example : Full Short Description
# --------------------------------------------------------------------
# 0x010200a0 : 1.2-alpha0 1.2a0 Use while developing next release
# 0x010200a1 : 1.2-alpha1 1.2a1 First alpha release
# 0x010200b1 : 1.2-beta1 1.2b1 First beta release
# 0x010200c1 : 1.2-rc1 1.2rc1 First release candidate
# 0x010200f0 : 1.2.0 1.2.0 Final release
# 0x010201f0 : 1.2.1 1.2.1 Patch release
##
__package__ = "novelwriter"
__copyright__ = "Copyright 20182023, Veronica Berglyd Olsen"
__license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen"
__maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net"
__version__ = "2.1-rc1"
__hexversion__ = "0x020100c1"
__date__ = "2023-08-31"
__status__ = "Stable"
__domain__ = "novelwriter.io"
logger = logging.getLogger(__name__)
##
# Main Program
##
# Global config singleton
CONFIG = Config()
SHARED = SharedData()
def main(sysArgs: list | None = None):
"""Parse command line, set up logging, and launch main GUI."""
if sysArgs is None:
sysArgs = sys.argv[1:]
# Valid Input Options
shortOpt = "hv"
longOpt = [
"help",
"version",
"info",
"debug",
"style=",
"config=",
"data=",
"testmode",
]
helpMsg = (
f"novelWriter {__version__} ({__date__})\n"
f"{__copyright__}\n"
"\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"
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
"GNU General Public Licence for more details.\n"
"\n"
"Usage:\n"
" -h, --help Print this message.\n"
" -v, --version Print program version and exit.\n"
" --info Print additional runtime information.\n"
" --debug Print debug output. Includes --info.\n"
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
" --config= Alternative config file.\n"
" --data= Alternative user data path.\n"
)
# Defaults
logLevel = logging.WARN
logFormat = "{levelname:8} {message:}"
confPath = None
dataPath = None
testMode = False
qtStyle = "Fusion"
cmdOpen = None
# Parse Options
try:
inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt)
except getopt.GetoptError as exc:
print(helpMsg)
print(f"ERROR: {str(exc)}")
sys.exit(2)
if len(inRemain) > 0:
cmdOpen = inRemain[0]
for inOpt, inArg in inOpts:
if inOpt in ("-h", "--help"):
print(helpMsg)
sys.exit(0)
elif inOpt in ("-v", "--version"):
print("novelWriter Version %s [%s]" % (__version__, __date__))
sys.exit(0)
elif inOpt == "--info":
logLevel = logging.INFO
elif inOpt == "--debug":
logLevel = logging.DEBUG
logFormat = "[{asctime:}] {filename:>17}:{lineno:<4d} {levelname:8} {message:}"
elif inOpt == "--style":
qtStyle = inArg
elif inOpt == "--config":
confPath = inArg
elif inOpt == "--data":
dataPath = inArg
elif inOpt == "--testmode":
testMode = True
# Setup Logging
pkgLogger = logging.getLogger(__package__)
pkgLogger.setLevel(logLevel)
if len(pkgLogger.handlers) == 0:
# Make sure we only create one logger (mostly an issue with tests)
cHandle = logging.StreamHandler()
cHandle.setFormatter(logging.Formatter(fmt=logFormat, style="{"))
pkgLogger.addHandler(cHandle)
logger.info("Starting novelWriter %s (%s) %s", __version__, __hexversion__, __date__)
# Check Packages and Versions
errorData = []
errorCode = 0
if sys.hexversion < 0x030800f0:
errorData.append(
"At least Python 3.8 is required, found %s" % CONFIG.verPyString
)
errorCode |= 0x04
if CONFIG.verQtValue < 0x050a00:
errorData.append(
"At least Qt5 version 5.10 is required, found %s" % CONFIG.verQtString
)
errorCode |= 0x08
if CONFIG.verPyQtValue < 0x050a00:
errorData.append(
"At least PyQt5 version 5.10 is required, found %s" % CONFIG.verPyQtString
)
errorCode |= 0x10
if errorData:
errApp = QApplication([])
errDlg = QErrorMessage()
errDlg.resize(500, 300)
errDlg.showMessage((
"<h3>A critical error was encountered</h3>"
"<p>novelWriter cannot start due to the following issues:<p>"
"<p>&nbsp;-&nbsp;%s</p>"
"<p>Shutting down ...</p>"
) % (
"<br>&nbsp;-&nbsp;".join(errorData)
))
for errLine in errorData:
logger.critical(errLine)
errApp.exec_()
sys.exit(errorCode)
# Finish initialising config
CONFIG.initConfig(confPath, dataPath)
if CONFIG.osDarwin:
try:
from Foundation import NSBundle
bundle = NSBundle.mainBundle()
info = bundle.localizedInfoDictionary() or bundle.infoDictionary()
info["CFBundleName"] = "novelWriter"
except Exception:
logger.error("Failed to set application name")
logException()
elif CONFIG.osWindows:
try:
import ctypes
appID = f"io.novelwriter.{__version__}"
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appID)
except Exception:
logger.error("Failed to set application name")
logException()
# Import GUI (after dependency checks), and launch
from novelwriter.guimain import GuiMain
if testMode:
nwGUI = GuiMain()
return nwGUI
else:
nwApp = QApplication([CONFIG.appName, (f"-style={qtStyle}")])
nwApp.setApplicationName(CONFIG.appName)
nwApp.setApplicationVersion(__version__)
nwApp.setOrganizationDomain(__domain__)
nwApp.setOrganizationName(__domain__)
nwApp.setDesktopFileName(CONFIG.appName)
# Connect the exception handler before making the main GUI
sys.excepthook = exceptionHandler
# Run Config steps that require the QApplication
CONFIG.initLocalisation(nwApp)
CONFIG.setTextFont(CONFIG.textFont, CONFIG.textSize) # Makes sure these are valid
# Launch main GUI
nwGUI = GuiMain()
nwGUI.postLaunchTasks(cmdOpen)
sys.exit(nwApp.exec_())
# END Function main