Merge branch 'main' into features/footnotes
This commit is contained in:
@@ -31,9 +31,9 @@ documentation.
|
|||||||
|
|
||||||
## Implementation
|
## Implementation
|
||||||
|
|
||||||
novelWriter is written with Python 3 (3.8+) using Qt5 and PyQt5 (5.10+), and is released on Linux,
|
novelWriter is written with Python 3 (3.8+) using Qt5 and PyQt5 (5.15 only), and is released on
|
||||||
Windows and macOS. It can in principle run on any Operating System that also supports Qt, PyQt and
|
Linux, Windows and macOS. It can in principle run on any Operating System that also supports Qt,
|
||||||
Python.
|
PyQt and Python.
|
||||||
|
|
||||||
## Project Contributions
|
## Project Contributions
|
||||||
|
|
||||||
|
|||||||
@@ -39,11 +39,9 @@ The following Python packages are needed to run all features of novelWriter:
|
|||||||
* ``PyQt5`` – needed for connecting with the Qt5 libraries.
|
* ``PyQt5`` – needed for connecting with the Qt5 libraries.
|
||||||
* ``PyEnchant`` – needed for spell checking (optional).
|
* ``PyEnchant`` – needed for spell checking (optional).
|
||||||
|
|
||||||
PyQt/Qt should be at least 5.10, but ideally 5.13 or higher for all features to work. For instance,
|
PyQt/Qt must be at least 5.15.0. If you want spell checking, you must install the ``PyEnchant``
|
||||||
searching using regular expressions with full Unicode support requires 5.13.
|
package. The spell check library must be at least 3.0 to work with Windows. On Linux, 2.0 also
|
||||||
|
works fine.
|
||||||
If you want spell checking, you must install the ``PyEnchant`` package. The spell check library
|
|
||||||
must be at least 3.0 to work with Windows. On Linux, 2.0 also works fine.
|
|
||||||
|
|
||||||
If you install from PyPi, these dependencies should be installed automatically. If you install from
|
If you install from PyPi, these dependencies should be installed automatically. If you install from
|
||||||
source, dependencies can still be installed from PyPi with:
|
source, dependencies can still be installed from PyPi with:
|
||||||
@@ -55,7 +53,7 @@ source, dependencies can still be installed from PyPi with:
|
|||||||
.. note::
|
.. note::
|
||||||
On Linux distros, the Qt library is usually split up into multiple packages. In some cases,
|
On Linux distros, the Qt library is usually split up into multiple packages. In some cases,
|
||||||
secondary dependencies may not be installed automatically. For novelWriter, the library files
|
secondary dependencies may not be installed automatically. For novelWriter, the library files
|
||||||
for renderring the SVG icons may be left out and needs to be installed manually. This is the
|
for rendering the SVG icons may be left out and needs to be installed manually. This is the
|
||||||
case on for instance Arch Linux.
|
case on for instance Arch Linux.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -47,9 +47,9 @@ __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.5a1"
|
__version__ = "2.5a2"
|
||||||
__hexversion__ = "0x020500a1"
|
__hexversion__ = "0x020500a1"
|
||||||
__date__ = "2024-04-13"
|
__date__ = "2024-04-16"
|
||||||
__status__ = "Stable"
|
__status__ = "Stable"
|
||||||
__domain__ = "novelwriter.io"
|
__domain__ = "novelwriter.io"
|
||||||
|
|
||||||
@@ -167,14 +167,14 @@ def main(sysArgs: list | None = None) -> GuiMain | None:
|
|||||||
"At least Python 3.8 is required, found %s" % CONFIG.verPyString
|
"At least Python 3.8 is required, found %s" % CONFIG.verPyString
|
||||||
)
|
)
|
||||||
errorCode |= 0x04
|
errorCode |= 0x04
|
||||||
if CONFIG.verQtValue < 0x050a00:
|
if CONFIG.verQtValue < 0x050f00:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least Qt5 version 5.10 is required, found %s" % CONFIG.verQtString
|
"At least Qt5 version 5.15.0 is required, found %s" % CONFIG.verQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x08
|
errorCode |= 0x08
|
||||||
if CONFIG.verPyQtValue < 0x050a00:
|
if CONFIG.verPyQtValue < 0x050f00:
|
||||||
errorData.append(
|
errorData.append(
|
||||||
"At least PyQt5 version 5.10 is required, found %s" % CONFIG.verPyQtString
|
"At least PyQt5 version 5.15.0 is required, found %s" % CONFIG.verPyQtString
|
||||||
)
|
)
|
||||||
errorCode |= 0x10
|
errorCode |= 0x10
|
||||||
|
|
||||||
|
|||||||
@@ -370,17 +370,7 @@ class DocSearch:
|
|||||||
def _buildPattern(self, search: str) -> str:
|
def _buildPattern(self, search: str) -> str:
|
||||||
"""Build the search pattern string."""
|
"""Build the search pattern string."""
|
||||||
if self._escape:
|
if self._escape:
|
||||||
if CONFIG.verQtValue >= 0x050f00:
|
search = QRegularExpression.escape(search)
|
||||||
search = QRegularExpression.escape(search)
|
|
||||||
else:
|
|
||||||
# For older Qt versions, we escape manually
|
|
||||||
escaped = ""
|
|
||||||
for c in search:
|
|
||||||
if c.isalnum() or c == "_":
|
|
||||||
escaped += c
|
|
||||||
else:
|
|
||||||
escaped += f"\\{c}"
|
|
||||||
search = escaped
|
|
||||||
if self._words:
|
if self._words:
|
||||||
search = f"(?:^|\\b){search}(?:$|\\b)"
|
search = f"(?:^|\\b){search}(?:$|\\b)"
|
||||||
return search
|
return search
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ from time import time
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from PyQt5.QtCore import (
|
from PyQt5.QtCore import (
|
||||||
pyqtSignal, pyqtSlot, QObject, QPoint, QRegExp, QRegularExpression,
|
pyqtSignal, pyqtSlot, QObject, QPoint, QRegularExpression, QRunnable, Qt,
|
||||||
QRunnable, Qt, QTimer
|
QTimer
|
||||||
)
|
)
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QColor, QCursor, QFont, QKeyEvent, QKeySequence, QMouseEvent, QPalette,
|
QColor, QCursor, QFont, QKeyEvent, QKeySequence, QMouseEvent, QPalette,
|
||||||
@@ -2568,32 +2568,18 @@ class GuiDocEditSearch(QFrame):
|
|||||||
# Getters
|
# Getters
|
||||||
##
|
##
|
||||||
|
|
||||||
def getSearchObject(self) -> str | QRegularExpression | QRegExp:
|
def getSearchObject(self) -> str | QRegularExpression:
|
||||||
"""Return the current search text either as text or as a regular
|
"""Return the current search text either as text or as a regular
|
||||||
expression object.
|
expression object.
|
||||||
"""
|
"""
|
||||||
text = self.searchBox.text()
|
text = self.searchBox.text()
|
||||||
if CONFIG.searchRegEx:
|
if CONFIG.searchRegEx:
|
||||||
# Using the Unicode-capable QRegularExpression class was
|
rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
|
||||||
# only added in Qt 5.13. Otherwise, 5.3 and up supports
|
if not CONFIG.searchCase:
|
||||||
# only the QRegExp class.
|
rxOpt |= QRegularExpression.PatternOption.CaseInsensitiveOption
|
||||||
if CONFIG.verQtValue >= 0x050d00:
|
regEx = QRegularExpression(text, rxOpt)
|
||||||
rxOpt = QRegularExpression.PatternOption.UseUnicodePropertiesOption
|
self._alertSearchValid(regEx.isValid())
|
||||||
if not CONFIG.searchCase:
|
return regEx
|
||||||
rxOpt |= QRegularExpression.PatternOption.CaseInsensitiveOption
|
|
||||||
regEx = QRegularExpression(text, rxOpt)
|
|
||||||
self._alertSearchValid(regEx.isValid())
|
|
||||||
return regEx
|
|
||||||
else: # pragma: no cover
|
|
||||||
# >= 50300 to < 51300
|
|
||||||
if CONFIG.searchCase:
|
|
||||||
rxOpt = Qt.CaseSensitivity.CaseSensitive
|
|
||||||
else:
|
|
||||||
rxOpt = Qt.CaseSensitivity.CaseInsensitive
|
|
||||||
regEx = QRegExp(text, rxOpt)
|
|
||||||
self._alertSearchValid(regEx.isValid())
|
|
||||||
return regEx
|
|
||||||
|
|
||||||
return text
|
return text
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|||||||
@@ -209,9 +209,8 @@ class GuiDocViewerPanel(QWidget):
|
|||||||
|
|
||||||
def _updateTabVisibility(self) -> None:
|
def _updateTabVisibility(self) -> None:
|
||||||
"""Hide class tabs with no content."""
|
"""Hide class tabs with no content."""
|
||||||
if CONFIG.verQtValue >= 0x050f00:
|
for tClass, cTab in self.kwTabs.items():
|
||||||
for tClass, cTab in self.kwTabs.items():
|
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
|
||||||
self.mainTabs.setTabVisible(self.idTabs[tClass], cTab.countEntries() > 0)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _loadAllTags(self) -> None:
|
def _loadAllTags(self) -> None:
|
||||||
|
|||||||
+61
-348
@@ -33,6 +33,8 @@ import datetime
|
|||||||
import subprocess
|
import subprocess
|
||||||
import email.utils
|
import email.utils
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
OS_NONE = 0
|
OS_NONE = 0
|
||||||
OS_LINUX = 1
|
OS_LINUX = 1
|
||||||
OS_WIN = 2
|
OS_WIN = 2
|
||||||
@@ -54,16 +56,15 @@ def extractVersion(beQuiet: bool = False) -> tuple[str, str, str]:
|
|||||||
numVers = "0"
|
numVers = "0"
|
||||||
hexVers = "0x0"
|
hexVers = "0x0"
|
||||||
relDate = "Unknown"
|
relDate = "Unknown"
|
||||||
initFile = os.path.join("novelwriter", "__init__.py")
|
initFile = Path("novelwriter") / "__init__.py"
|
||||||
try:
|
try:
|
||||||
with open(initFile, mode="r", encoding="utf-8") as inFile:
|
for aLine in initFile.read_text(encoding="utf-8").splitlines():
|
||||||
for aLine in inFile:
|
if aLine.startswith("__version__"):
|
||||||
if aLine.startswith("__version__"):
|
numVers = getValue((aLine))
|
||||||
numVers = getValue((aLine))
|
if aLine.startswith("__hexversion__"):
|
||||||
if aLine.startswith("__hexversion__"):
|
hexVers = getValue((aLine))
|
||||||
hexVers = getValue((aLine))
|
if aLine.startswith("__date__"):
|
||||||
if aLine.startswith("__date__"):
|
relDate = getValue((aLine))
|
||||||
relDate = getValue((aLine))
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
print("Could not read file: %s" % initFile)
|
print("Could not read file: %s" % initFile)
|
||||||
print(str(exc))
|
print(str(exc))
|
||||||
@@ -88,26 +89,23 @@ def stripVersion(version: str) -> str:
|
|||||||
|
|
||||||
def readFile(fileName: str) -> str:
|
def readFile(fileName: str) -> str:
|
||||||
"""Read an entire file and return as a string."""
|
"""Read an entire file and return as a string."""
|
||||||
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
return Path(fileName).read_text(encoding="utf-8")
|
||||||
return inFile.read()
|
|
||||||
|
|
||||||
|
|
||||||
def writeFile(fileName: str, writeText: str) -> None:
|
def writeFile(fileName: str, text: str) -> None:
|
||||||
"""Write string to file."""
|
"""Write string to file."""
|
||||||
with open(fileName, mode="w+", encoding="utf-8") as outFile:
|
Path(fileName).write_text(text, encoding="utf-8")
|
||||||
outFile.write(writeText)
|
return
|
||||||
|
|
||||||
|
|
||||||
def toUpload(srcPath: str, dstName: str | None = None) -> None:
|
def toUpload(srcPath: str | Path, dstName: str | None = None) -> None:
|
||||||
"""Copy a file produced by one of the build functions to the upload
|
"""Copy a file produced by one of the build functions to the upload
|
||||||
directory. The file can optionally be given a new name.
|
directory. The file can optionally be given a new name.
|
||||||
"""
|
"""
|
||||||
uplDir = "dist_upload"
|
uplDir = Path("dist_upload")
|
||||||
if not os.path.isdir(uplDir):
|
uplDir.mkdir(exist_ok=True)
|
||||||
os.mkdir(uplDir)
|
srcPath = Path(srcPath)
|
||||||
if dstName is None:
|
shutil.copyfile(srcPath, uplDir / (dstName or srcPath.name))
|
||||||
dstName = os.path.basename(srcPath)
|
|
||||||
shutil.copyfile(srcPath, os.path.join(uplDir, dstName))
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
@@ -561,43 +559,24 @@ def importI18nUpdates(sysArgs: list[str]) -> None:
|
|||||||
# Make Minimal Package (minimal-zip)
|
# Make Minimal Package (minimal-zip)
|
||||||
##
|
##
|
||||||
|
|
||||||
def makeMinimalPackage(targetOS: int) -> None:
|
def makeWindowsZip() -> None:
|
||||||
"""Pack the core source file in a single zip file."""
|
"""Pack the core source file in a single zip file."""
|
||||||
from zipfile import ZipFile, ZIP_DEFLATED
|
from zipfile import ZipFile, ZIP_DEFLATED
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("Building Minimal ZIP File")
|
print("Building Windows ZIP File")
|
||||||
print("=========================")
|
print("=========================")
|
||||||
|
|
||||||
bldDir = "dist_minimal"
|
bldDir = "dist_minimal"
|
||||||
if not os.path.isdir(bldDir):
|
if not os.path.isdir(bldDir):
|
||||||
os.mkdir(bldDir)
|
os.mkdir(bldDir)
|
||||||
|
|
||||||
if targetOS == OS_LINUX:
|
|
||||||
targName = "-linux"
|
|
||||||
print("Target OS: Linux")
|
|
||||||
elif targetOS == OS_DARWIN:
|
|
||||||
targName = "-darwin"
|
|
||||||
print("Target OS: Darwin")
|
|
||||||
elif targetOS == OS_WIN:
|
|
||||||
targName = "-win"
|
|
||||||
print("Target OS: Windows")
|
|
||||||
else:
|
|
||||||
targName = ""
|
|
||||||
print("")
|
|
||||||
|
|
||||||
# Check Additional Assets
|
|
||||||
# =======================
|
|
||||||
|
|
||||||
if not checkAssetsExist():
|
if not checkAssetsExist():
|
||||||
print("ERROR: Missing build assets")
|
print("ERROR: Missing build assets")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Build Minimal Zip
|
|
||||||
# =================
|
|
||||||
|
|
||||||
pkgVers, _, _ = extractVersion()
|
pkgVers, _, _ = extractVersion()
|
||||||
zipFile = f"novelwriter-{pkgVers}-minimal{targName}.zip"
|
zipFile = f"novelwriter-{pkgVers}-minimal-win.zip"
|
||||||
outFile = os.path.join(bldDir, zipFile)
|
outFile = os.path.join(bldDir, zipFile)
|
||||||
if os.path.isfile(outFile):
|
if os.path.isfile(outFile):
|
||||||
os.unlink(outFile)
|
os.unlink(outFile)
|
||||||
@@ -627,26 +606,8 @@ def makeMinimalPackage(targetOS: int) -> None:
|
|||||||
continue
|
continue
|
||||||
zipObj.write(os.path.join(nRoot, aFile))
|
zipObj.write(os.path.join(nRoot, aFile))
|
||||||
|
|
||||||
if targetOS == OS_WIN:
|
zipObj.write("novelWriter.py", "novelWriter.pyw")
|
||||||
zipObj.write("novelWriter.py", "novelWriter.pyw")
|
print("Added: novelWriter.pyw")
|
||||||
print("Added: novelWriter.pyw")
|
|
||||||
zipObj.write(os.path.join("setup", "windows_install.bat"), "windows_install.bat")
|
|
||||||
print("Added: windows_install.bat")
|
|
||||||
zipObj.write(os.path.join("setup", "windows_uninstall.bat"), "windows_uninstall.bat")
|
|
||||||
print("Added: windows_uninstall.bat")
|
|
||||||
|
|
||||||
else: # Linux and Mac
|
|
||||||
# Add icons
|
|
||||||
for nRoot, _, nFiles in os.walk(os.path.join("setup", "data")):
|
|
||||||
print("Added: %s/* [Files: %d]" % (nRoot, len(nFiles)))
|
|
||||||
for aFile in nFiles:
|
|
||||||
zipObj.write(os.path.join(nRoot, aFile))
|
|
||||||
|
|
||||||
zipObj.write(os.path.join("setup", "description_pypi.md"))
|
|
||||||
print("Added: setup/description_pypi.md")
|
|
||||||
|
|
||||||
zipObj.write("novelWriter.py")
|
|
||||||
print("Added: novelWriter.py")
|
|
||||||
|
|
||||||
for aFile in rootFiles:
|
for aFile in rootFiles:
|
||||||
print("Added: %s" % aFile)
|
print("Added: %s" % aFile)
|
||||||
@@ -658,14 +619,9 @@ def makeMinimalPackage(targetOS: int) -> None:
|
|||||||
print("")
|
print("")
|
||||||
print("Created File: %s" % outFile)
|
print("Created File: %s" % outFile)
|
||||||
|
|
||||||
# Create Checksum File
|
|
||||||
# ====================
|
|
||||||
|
|
||||||
shaFile = makeCheckSum(zipFile, cwd=bldDir)
|
shaFile = makeCheckSum(zipFile, cwd=bldDir)
|
||||||
|
|
||||||
toUpload(outFile)
|
toUpload(outFile)
|
||||||
toUpload(shaFile)
|
toUpload(shaFile)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -675,8 +631,10 @@ def makeMinimalPackage(targetOS: int) -> None:
|
|||||||
# Make Debian Package (build-deb)
|
# Make Debian Package (build-deb)
|
||||||
##
|
##
|
||||||
|
|
||||||
def makeDebianPackage(signKey: str | None = None, sourceBuild: bool = False,
|
def makeDebianPackage(
|
||||||
distName: str = "unstable", buildName: str = "") -> str:
|
signKey: str | None = None, sourceBuild: bool = False,
|
||||||
|
distName: str = "unstable", buildName: str = "", oldSetuptools: bool = False
|
||||||
|
) -> str:
|
||||||
"""Build a Debian package."""
|
"""Build a Debian package."""
|
||||||
print("")
|
print("")
|
||||||
print("Build Debian Package")
|
print("Build Debian Package")
|
||||||
@@ -778,11 +736,27 @@ def makeDebianPackage(signKey: str | None = None, sourceBuild: bool = False,
|
|||||||
))
|
))
|
||||||
print("Wrote: setup.py")
|
print("Wrote: setup.py")
|
||||||
|
|
||||||
setupCfg = readFile("pyproject.toml").replace(
|
if oldSetuptools:
|
||||||
"setup/description_pypi.md", "data/description_short.txt"
|
# This is needed for Ubuntu up to 22.04
|
||||||
)
|
setupCfg = readFile("setup/launchpad_setup.cfg").replace(
|
||||||
writeFile(f"{outDir}/pyproject.toml", setupCfg)
|
"file: setup/description_pypi.md", "file: data/description_short.txt"
|
||||||
print("Wrote: pyproject.toml")
|
)
|
||||||
|
writeFile(f"{outDir}/setup.cfg", setupCfg)
|
||||||
|
print("Wrote: setup.cfg")
|
||||||
|
|
||||||
|
writeFile(f"{outDir}/pyproject.toml", (
|
||||||
|
"[build-system]\n"
|
||||||
|
"requires = [\"setuptools\"]\n"
|
||||||
|
"build-backend = \"setuptools.build_meta\"\n"
|
||||||
|
))
|
||||||
|
print("Wrote: pyproject.toml")
|
||||||
|
|
||||||
|
else:
|
||||||
|
pyProject = readFile("pyproject.toml").replace(
|
||||||
|
"setup/description_pypi.md", "data/description_short.txt"
|
||||||
|
)
|
||||||
|
writeFile(f"{outDir}/pyproject.toml", pyProject)
|
||||||
|
print("Wrote: pyproject.toml")
|
||||||
|
|
||||||
# Copy/Write Debian Files
|
# Copy/Write Debian Files
|
||||||
# =======================
|
# =======================
|
||||||
@@ -860,15 +834,14 @@ def makeForLaunchpad(doSign: bool = False, isFirst: bool = False) -> None:
|
|||||||
bldNum = "0"
|
bldNum = "0"
|
||||||
|
|
||||||
distLoop = [
|
distLoop = [
|
||||||
("20.04", "focal"),
|
("22.04", "jammy", True),
|
||||||
("22.04", "jammy"),
|
("23.10", "mantic", False),
|
||||||
("23.10", "mantic"),
|
("24.04", "noble", False),
|
||||||
("24.04", "noble"),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
print("Building Ubuntu packages for:")
|
print("Building Ubuntu packages for:")
|
||||||
print("")
|
print("")
|
||||||
for distNum, codeName in distLoop:
|
for distNum, codeName, _ in distLoop:
|
||||||
print(f" * Ubuntu {distNum} {codeName.title()}")
|
print(f" * Ubuntu {distNum} {codeName.title()}")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
@@ -881,13 +854,14 @@ def makeForLaunchpad(doSign: bool = False, isFirst: bool = False) -> None:
|
|||||||
print("")
|
print("")
|
||||||
|
|
||||||
dputCmd = []
|
dputCmd = []
|
||||||
for distNum, codeName in distLoop:
|
for distNum, codeName, oldSetup in distLoop:
|
||||||
buildName = f"ubuntu{distNum}.{bldNum}"
|
buildName = f"ubuntu{distNum}.{bldNum}"
|
||||||
dCmd = makeDebianPackage(
|
dCmd = makeDebianPackage(
|
||||||
signKey=signKey,
|
signKey=signKey,
|
||||||
sourceBuild=True,
|
sourceBuild=True,
|
||||||
distName=codeName,
|
distName=codeName,
|
||||||
buildName=buildName
|
buildName=buildName,
|
||||||
|
oldSetuptools=oldSetup,
|
||||||
)
|
)
|
||||||
dputCmd.append(dCmd)
|
dputCmd.append(dCmd)
|
||||||
|
|
||||||
@@ -1555,226 +1529,6 @@ def xdgUninstall() -> None:
|
|||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# WIN Installation (win-install)
|
|
||||||
##
|
|
||||||
|
|
||||||
def winInstall() -> None:
|
|
||||||
"""Will attempt to install icons and make a launcher for Windows."""
|
|
||||||
if sys.platform != "win32":
|
|
||||||
raise Exception("This method only runs on Windows")
|
|
||||||
|
|
||||||
import winreg
|
|
||||||
try:
|
|
||||||
import win32com.client
|
|
||||||
except ImportError:
|
|
||||||
print(
|
|
||||||
"ERROR: Package 'pywin32' is missing on this system.\n"
|
|
||||||
" Please run 'pkgutils.py pip' to automatically install\n"
|
|
||||||
" dependecies, or run 'pip install --user pywin32'."
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Windows Install")
|
|
||||||
print("===============")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
numVers, hexVers, _ = extractVersion()
|
|
||||||
nwTesting = not hexVers[-2] == "f"
|
|
||||||
wShell = win32com.client.Dispatch("WScript.Shell")
|
|
||||||
|
|
||||||
if nwTesting:
|
|
||||||
linkName = "novelWriter Testing %s.lnk" % numVers
|
|
||||||
else:
|
|
||||||
linkName = "novelWriter %s.lnk" % numVers
|
|
||||||
|
|
||||||
desktopDir = wShell.SpecialFolders("Desktop")
|
|
||||||
desktopIcon = os.path.join(desktopDir, linkName)
|
|
||||||
|
|
||||||
startMenuDir = wShell.SpecialFolders("StartMenu")
|
|
||||||
startMenuProg = os.path.join(startMenuDir, "Programs", "novelWriter")
|
|
||||||
startMenuIcon = os.path.join(startMenuProg, linkName)
|
|
||||||
|
|
||||||
pythonDir = os.path.dirname(sys.executable)
|
|
||||||
pythonExe = os.path.join(pythonDir, "pythonw.exe")
|
|
||||||
|
|
||||||
targetDir = os.path.abspath(os.path.dirname(__file__))
|
|
||||||
targetPy = os.path.join(targetDir, "novelWriter.pyw")
|
|
||||||
targetIcon = os.path.join(targetDir, "novelwriter", "assets", "icons", "novelwriter.ico")
|
|
||||||
|
|
||||||
if not os.path.isfile(targetPy):
|
|
||||||
shutil.copy2(os.path.join(targetDir, "novelWriter.py"), targetPy)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Collecting Info ...")
|
|
||||||
print("Desktop Folder: %s" % desktopDir)
|
|
||||||
print("Start Menu Folder: %s" % startMenuDir)
|
|
||||||
print("Python Executable: %s" % pythonExe)
|
|
||||||
print("Target Executable: %s" % targetPy)
|
|
||||||
print("Target Icon: %s" % targetIcon)
|
|
||||||
print("")
|
|
||||||
|
|
||||||
print("Creating Links ...")
|
|
||||||
if os.path.isfile(desktopIcon):
|
|
||||||
os.unlink(desktopIcon)
|
|
||||||
print("Deleted: %s" % desktopIcon)
|
|
||||||
|
|
||||||
if os.path.isdir(startMenuProg):
|
|
||||||
for oldIcon in os.listdir(startMenuProg):
|
|
||||||
oldPath = os.path.join(startMenuProg, oldIcon)
|
|
||||||
if not oldIcon.startswith("novelWriter"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
isTesting = oldIcon.startswith("novelWriter Testing")
|
|
||||||
if isTesting and nwTesting:
|
|
||||||
os.unlink(oldPath)
|
|
||||||
print("Deleted: %s" % oldPath)
|
|
||||||
if not isTesting and not nwTesting:
|
|
||||||
os.unlink(oldPath)
|
|
||||||
print("Deleted: %s" % oldPath)
|
|
||||||
|
|
||||||
else:
|
|
||||||
os.mkdir(startMenuProg)
|
|
||||||
print("Created: %s" % startMenuProg)
|
|
||||||
|
|
||||||
wShortcut = wShell.CreateShortCut(desktopIcon)
|
|
||||||
wShortcut.TargetPath = targetPy
|
|
||||||
wShortcut.WorkingDirectory = targetDir
|
|
||||||
wShortcut.IconLocation = targetIcon
|
|
||||||
wShortcut.WindowStyle = 1
|
|
||||||
wShortcut.save()
|
|
||||||
print("Created: %s" % desktopIcon)
|
|
||||||
|
|
||||||
wShortcut = wShell.CreateShortCut(startMenuIcon)
|
|
||||||
wShortcut.TargetPath = targetPy
|
|
||||||
wShortcut.WorkingDirectory = targetDir
|
|
||||||
wShortcut.IconLocation = targetIcon
|
|
||||||
wShortcut.WindowStyle = 1
|
|
||||||
wShortcut.save()
|
|
||||||
print("Created: %s" % startMenuIcon)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Creating registry keys ...")
|
|
||||||
|
|
||||||
def setKey(kPath: str, kName: str, kVal: str) -> None:
|
|
||||||
winreg.CreateKey(winreg.HKEY_CURRENT_USER, kPath)
|
|
||||||
regKey = winreg.OpenKey(winreg.HKEY_CURRENT_USER, kPath, 0, winreg.KEY_WRITE)
|
|
||||||
winreg.SetValueEx(regKey, kName, 0, winreg.REG_SZ, kVal)
|
|
||||||
winreg.CloseKey(regKey)
|
|
||||||
|
|
||||||
mimeIcon = os.path.join(
|
|
||||||
targetDir, "novelwriter", "assets", "icons", "x-novelwriter-project.ico"
|
|
||||||
)
|
|
||||||
mimeExec = '"%s" "%s" "%%1"' % (pythonExe, targetPy)
|
|
||||||
|
|
||||||
try:
|
|
||||||
setKey(r"Software\Classes\.nwx\OpenWithProgids", "novelWriterProject.nwx", "")
|
|
||||||
setKey(r"Software\Classes\novelWriterProject.nwx", "", "novelWriter Project File")
|
|
||||||
setKey(r"Software\Classes\novelWriterProject.nwx\DefaultIcon", "", mimeIcon)
|
|
||||||
setKey(r"Software\Classes\novelWriterProject.nwx\shell\open\command", "", mimeExec)
|
|
||||||
setKey(r"Software\Classes\Applications\novelWriter.pyw\SupportedTypes", ".nwx", "")
|
|
||||||
except WindowsError:
|
|
||||||
print("ERROR: Failed to set registry keys.")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Done!")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# WIN Uninstallation (win-uninstall)
|
|
||||||
##
|
|
||||||
|
|
||||||
def winUninstall() -> None:
|
|
||||||
"""Will attempt to uninstall icons previously installed."""
|
|
||||||
if sys.platform != "win32":
|
|
||||||
raise Exception("This method only runs on Windows")
|
|
||||||
|
|
||||||
import winreg
|
|
||||||
try:
|
|
||||||
import win32com.client
|
|
||||||
except ImportError:
|
|
||||||
print(
|
|
||||||
"ERROR: Package 'pywin32' is missing on this system.\n"
|
|
||||||
" Please run 'pip install --user pywin32' to install it."
|
|
||||||
)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Windows Uninstall")
|
|
||||||
print("=================")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
numVers, hexVers, _ = extractVersion()
|
|
||||||
nwTesting = not hexVers[-2] == "f"
|
|
||||||
wShell = win32com.client.Dispatch("WScript.Shell")
|
|
||||||
|
|
||||||
if nwTesting:
|
|
||||||
linkName = "novelWriter Testing %s.lnk" % numVers
|
|
||||||
else:
|
|
||||||
linkName = "novelWriter %s.lnk" % numVers
|
|
||||||
|
|
||||||
desktopDir = wShell.SpecialFolders("Desktop")
|
|
||||||
desktopIcon = os.path.join(desktopDir, linkName)
|
|
||||||
|
|
||||||
startMenuDir = wShell.SpecialFolders("StartMenu")
|
|
||||||
startMenuProg = os.path.join(startMenuDir, "Programs", "novelWriter")
|
|
||||||
startMenuIcon = os.path.join(startMenuProg, linkName)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Deleting Links ...")
|
|
||||||
if os.path.isfile(desktopIcon):
|
|
||||||
os.unlink(desktopIcon)
|
|
||||||
print("Deleted: %s" % desktopIcon)
|
|
||||||
else:
|
|
||||||
print("Not Found: %s" % desktopIcon)
|
|
||||||
|
|
||||||
if os.path.isfile(startMenuIcon):
|
|
||||||
os.unlink(startMenuIcon)
|
|
||||||
print("Deleted: %s" % startMenuIcon)
|
|
||||||
else:
|
|
||||||
print("Not Found: %s" % startMenuIcon)
|
|
||||||
|
|
||||||
if os.path.isdir(startMenuProg):
|
|
||||||
if not os.listdir(startMenuProg):
|
|
||||||
os.rmdir(startMenuProg)
|
|
||||||
print("Deleted: %s" % startMenuProg)
|
|
||||||
else:
|
|
||||||
print("Not Empty: %s" % startMenuProg)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Removing registry keys ...")
|
|
||||||
|
|
||||||
keys = [
|
|
||||||
r"Software\Classes\novelWriterProject.nwx\shell\open\command",
|
|
||||||
r"Software\Classes\novelWriterProject.nwx\shell\open",
|
|
||||||
r"Software\Classes\novelWriterProject.nwx\shell",
|
|
||||||
r"Software\Classes\novelWriterProject.nwx\DefaultIcon",
|
|
||||||
r"Software\Classes\novelWriterProject.nwx",
|
|
||||||
r"Software\Classes\.nwx\OpenWithProgids",
|
|
||||||
r"Software\Classes\.nwx",
|
|
||||||
r"Software\Classes\Applications\novelWriter.pyw\SupportedTypes",
|
|
||||||
r"Software\Classes\Applications\novelWriter.pyw",
|
|
||||||
]
|
|
||||||
|
|
||||||
for aKey in keys:
|
|
||||||
try:
|
|
||||||
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, aKey)
|
|
||||||
print("Deleted: HKEY_CURRENT_USER\\%s" % aKey)
|
|
||||||
except WindowsError:
|
|
||||||
print("Not Found: HKEY_CURRENT_USER\\%s" % aKey)
|
|
||||||
|
|
||||||
print("")
|
|
||||||
print("Done!")
|
|
||||||
print("")
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
# Process Command Line
|
# Process Command Line
|
||||||
# =============================================================================================== #
|
# =============================================================================================== #
|
||||||
@@ -1795,19 +1549,6 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
sysArgs = sys.argv.copy()
|
sysArgs = sys.argv.copy()
|
||||||
|
|
||||||
# Set Target OS
|
|
||||||
if "--target-linux" in sysArgs:
|
|
||||||
sysArgs.remove("--target-linux")
|
|
||||||
targetOS = OS_LINUX
|
|
||||||
elif "--target-darwin" in sysArgs:
|
|
||||||
sysArgs.remove("--target-darwin")
|
|
||||||
targetOS = OS_DARWIN
|
|
||||||
elif "--target-win" in sysArgs:
|
|
||||||
sysArgs.remove("--target-win")
|
|
||||||
targetOS = OS_WIN
|
|
||||||
else:
|
|
||||||
targetOS = hostOS
|
|
||||||
|
|
||||||
# Sign package
|
# Sign package
|
||||||
if "--sign" in sysArgs:
|
if "--sign" in sysArgs:
|
||||||
sysArgs.remove("--sign")
|
sysArgs.remove("--sign")
|
||||||
@@ -1831,10 +1572,6 @@ if __name__ == "__main__":
|
|||||||
"novelWriter as a package on Linux, Mac and Windows. The available options",
|
"novelWriter as a package on Linux, Mac and Windows. The available options",
|
||||||
"are as follows:",
|
"are as follows:",
|
||||||
"",
|
"",
|
||||||
"Some of the commands can be targeted towards a different OS than the host OS.",
|
|
||||||
"To target the command, add one of '--target-linux', '--target-darwin' or",
|
|
||||||
"'--target-win'.",
|
|
||||||
"",
|
|
||||||
"General:",
|
"General:",
|
||||||
"",
|
"",
|
||||||
" help Print the help message.",
|
" help Print the help message.",
|
||||||
@@ -1855,10 +1592,8 @@ if __name__ == "__main__":
|
|||||||
"Python Packaging:",
|
"Python Packaging:",
|
||||||
"",
|
"",
|
||||||
" import-i18n Import updated i18n files from a zip file.",
|
" import-i18n Import updated i18n files from a zip file.",
|
||||||
" minimal-zip Creates a minimal zip file of the core application without",
|
" windows-zip Creates a minimal zip file of the core application without",
|
||||||
" all the other source files. Defaults to tailor the zip file",
|
" all the other source files. Used for Windows builds.",
|
||||||
" for the current OS, but accepts a target OS flag to build",
|
|
||||||
" for another OS.",
|
|
||||||
" build-deb Build a .deb package for Debian and Ubuntu. Add --sign to ",
|
" build-deb Build a .deb package for Debian and Ubuntu. Add --sign to ",
|
||||||
" sign package.",
|
" sign package.",
|
||||||
" build-ubuntu Build a .deb packages Launchpad. Add --sign to ",
|
" build-ubuntu Build a .deb packages Launchpad. Add --sign to ",
|
||||||
@@ -1870,17 +1605,11 @@ if __name__ == "__main__":
|
|||||||
"",
|
"",
|
||||||
"System Install:",
|
"System Install:",
|
||||||
"",
|
"",
|
||||||
" install Deprecated. Use pip or build.",
|
|
||||||
" xdg-install Install launcher and icons for freedesktop systems. Run as",
|
" xdg-install Install launcher and icons for freedesktop systems. Run as",
|
||||||
" root or with sudo for system-wide install, or as user for",
|
" root or with sudo for system-wide install, or as user for",
|
||||||
" single user install.",
|
" single user install.",
|
||||||
" xdg-uninstall Remove the launcher and icons for the current system as",
|
" xdg-uninstall Remove the launcher and icons for the current system as",
|
||||||
" installed by the 'xdg-install' command.",
|
" installed by the 'xdg-install' command.",
|
||||||
" win-install Install desktop icon, start menu icon, and registry entries",
|
|
||||||
" for file association with .nwx files for Windows systems.",
|
|
||||||
" win-uninstall Remove desktop icon, start menu icon, and registry keys,",
|
|
||||||
" for the current system. Note that it only removes icons for",
|
|
||||||
" the version number of the package the command is run from.",
|
|
||||||
"",
|
"",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1941,9 +1670,9 @@ if __name__ == "__main__":
|
|||||||
importI18nUpdates(sysArgs)
|
importI18nUpdates(sysArgs)
|
||||||
sys.exit(0) # Don't continue execution
|
sys.exit(0) # Don't continue execution
|
||||||
|
|
||||||
if "minimal-zip" in sysArgs:
|
if "windows-zip" in sysArgs:
|
||||||
sysArgs.remove("minimal-zip")
|
sysArgs.remove("windows-zip")
|
||||||
makeMinimalPackage(targetOS)
|
makeWindowsZip()
|
||||||
|
|
||||||
if "build-deb" in sysArgs:
|
if "build-deb" in sysArgs:
|
||||||
sysArgs.remove("build-deb")
|
sysArgs.remove("build-deb")
|
||||||
@@ -1997,20 +1726,4 @@ if __name__ == "__main__":
|
|||||||
else:
|
else:
|
||||||
xdgUninstall()
|
xdgUninstall()
|
||||||
|
|
||||||
if "win-install" in sysArgs:
|
|
||||||
sysArgs.remove("win-install")
|
|
||||||
if hostOS == OS_WIN:
|
|
||||||
winInstall()
|
|
||||||
else:
|
|
||||||
print("ERROR: Command 'win-install' can only be used on Windows")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
if "win-uninstall" in sysArgs:
|
|
||||||
sysArgs.remove("win-uninstall")
|
|
||||||
if hostOS == OS_WIN:
|
|
||||||
winUninstall()
|
|
||||||
else:
|
|
||||||
print("ERROR: Command 'win-uninstall' can only be used on Windows")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
# END Main
|
# END Main
|
||||||
|
|||||||
+1
-1
@@ -27,7 +27,7 @@ classifiers = [
|
|||||||
]
|
]
|
||||||
requires-python = ">=3.8"
|
requires-python = ">=3.8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pyqt5>=5.10",
|
"pyqt5>=5.15",
|
||||||
"pyenchant>=3.0.0",
|
"pyenchant>=3.0.0",
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
pyqt5>=5.10
|
pyqt5>=5.15
|
||||||
pyenchant>=3.0.0
|
pyenchant>=3.0.0
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ Source: novelwriter
|
|||||||
Maintainer: Veronica Berglyd Olsen <code@vkbo.net>
|
Maintainer: Veronica Berglyd Olsen <code@vkbo.net>
|
||||||
Section: text
|
Section: text
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), python3 (>=3.8), python3-pyqt5 (>= 5.10), python3-enchant (>= 2.0)
|
Build-Depends: dh-python, python3-setuptools, python3-all, debhelper (>= 9), python3 (>=3.8), python3-pyqt5 (>= 5.15), python3-enchant (>= 2.0)
|
||||||
Standards-Version: 4.5.1
|
Standards-Version: 4.5.1
|
||||||
Homepage: https://novelwriter.io
|
Homepage: https://novelwriter.io
|
||||||
X-Python3-Version: >= 3.8
|
X-Python3-Version: >= 3.8
|
||||||
|
|
||||||
Package: novelwriter
|
Package: novelwriter
|
||||||
Architecture: all
|
Architecture: all
|
||||||
Depends: ${misc:Depends}, ${python3:Depends}, python3 (>=3.8), python3-pyqt5 (>= 5.10), python3-enchant (>= 2.0)
|
Depends: ${misc:Depends}, ${python3:Depends}, python3 (>=3.8), python3-pyqt5 (>= 5.15), python3-enchant (>= 2.0)
|
||||||
Description: A markdown-like text editor for planning and writing novels
|
Description: A markdown-like text editor for planning and writing novels
|
||||||
novelWriter is a plain text editor designed for writing novels assembled from
|
novelWriter is a plain text editor designed for writing novels assembled from
|
||||||
many smaller text documents. It uses a minimal formatting syntax inspired by
|
many smaller text documents. It uses a minimal formatting syntax inspired by
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
[metadata]
|
||||||
|
name = novelWriter
|
||||||
|
version = attr: novelwriter.__version__
|
||||||
|
author = Veronica Berglyd Olsen
|
||||||
|
author_email = code@vkbo.net
|
||||||
|
description = A markdown-like text editor for planning and writing novels
|
||||||
|
url = https://novelwriter.io
|
||||||
|
long_description = file: setup/description_pypi.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
license_files = LICENSE.md
|
||||||
|
license = GNU General Public License v3
|
||||||
|
classifiers =
|
||||||
|
Programming Language :: Python :: 3 :: Only
|
||||||
|
Programming Language :: Python :: 3.8
|
||||||
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
|
Programming Language :: Python :: 3.12
|
||||||
|
Programming Language :: Python :: Implementation :: CPython
|
||||||
|
License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
||||||
|
Development Status :: 5 - Production/Stable
|
||||||
|
Operating System :: OS Independent
|
||||||
|
Intended Audience :: End Users/Desktop
|
||||||
|
Natural Language :: English
|
||||||
|
Topic :: Text Editors
|
||||||
|
project_urls =
|
||||||
|
Bug Tracker = https://github.com/vkbo/novelWriter/issues
|
||||||
|
Documentation = https://docs.novelwriter.io
|
||||||
|
Source Code = https://github.com/vkbo/novelWriter
|
||||||
|
|
||||||
|
[options]
|
||||||
|
zip_safe = False
|
||||||
|
python_requires = >=3.8
|
||||||
|
include_package_data = True
|
||||||
|
packages = find_namespace:
|
||||||
|
install_requires =
|
||||||
|
pyqt5>=5.15
|
||||||
|
pyenchant>=3.0.0
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
include = novelwriter*
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
gui_scripts =
|
||||||
|
novelwriter = novelwriter:main
|
||||||
|
|
||||||
|
[bdist_wheel]
|
||||||
|
universal = 0
|
||||||
@@ -22,12 +22,10 @@ python3 pkgutils.py qtlrelease manual sample
|
|||||||
deactivate
|
deactivate
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Building Minimal Packages"
|
echo " Building Windows Source Zip"
|
||||||
echo "================================================================================"
|
echo "================================================================================"
|
||||||
echo ""
|
echo ""
|
||||||
python3 pkgutils.py minimal-zip --target-win
|
python3 pkgutils.py windows-zip
|
||||||
# python3 pkgutils.py minimal-zip --target-linux
|
|
||||||
# python3 pkgutils.py minimal-zip --target-darwin
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo " Building Linux Packages"
|
echo " Building Linux Packages"
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
:: Check for Python Installation
|
|
||||||
echo.
|
|
||||||
echo Looking for Python
|
|
||||||
python --version 2>NUL
|
|
||||||
if errorlevel 1 goto errorNoPython
|
|
||||||
echo Python found. OK.
|
|
||||||
|
|
||||||
if exist pkgutils.py (
|
|
||||||
echo pkgutils.py found. OK.
|
|
||||||
) else (
|
|
||||||
cd ..
|
|
||||||
if exist pkgutils.py (
|
|
||||||
echo pkgutils.py found. OK.
|
|
||||||
) else (
|
|
||||||
goto errorNoSetup
|
|
||||||
)
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
:: Install the PyQt5 and pyenchant dependencies
|
|
||||||
pip install --user pywin32 -r requirements.txt
|
|
||||||
|
|
||||||
:: Create the desktop and start menu icons
|
|
||||||
python pkgutils.py win-install
|
|
||||||
|
|
||||||
pause
|
|
||||||
goto:eof
|
|
||||||
|
|
||||||
:errorNoPython
|
|
||||||
echo.
|
|
||||||
echo ERROR^: Python is not installed on your system, or cannot be found.
|
|
||||||
echo.
|
|
||||||
echo Please download and install it from https://www.python.org/downloads/
|
|
||||||
echo Also make sure the "Add Python to PATH" option is selected during installation.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
goto:eof
|
|
||||||
|
|
||||||
:errorNoSetup
|
|
||||||
echo.
|
|
||||||
echo ERROR^: Could not find the pkgutils.py script.
|
|
||||||
echo.
|
|
||||||
echo Make sure you run pkgutils.py from the novelWriter root folder.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
:: Check for Python Installation
|
|
||||||
echo.
|
|
||||||
echo Looking for Python
|
|
||||||
python --version 2>NUL
|
|
||||||
if errorlevel 1 goto errorNoPython
|
|
||||||
echo Python found. OK.
|
|
||||||
|
|
||||||
if exist pkgutils.py (
|
|
||||||
echo pkgutils.py found. OK.
|
|
||||||
) else (
|
|
||||||
cd ..
|
|
||||||
if exist pkgutils.py (
|
|
||||||
echo pkgutils.py found. OK.
|
|
||||||
) else (
|
|
||||||
goto errorNoSetup
|
|
||||||
)
|
|
||||||
)
|
|
||||||
echo.
|
|
||||||
|
|
||||||
echo Ready to remove the novelWriter icons, registry keys, and package dependencies.
|
|
||||||
set /P c=Are you sure you want to continue [y/n]?
|
|
||||||
if /I "%c%" EQU "y" goto doUninst
|
|
||||||
goto afterUninst
|
|
||||||
|
|
||||||
:doUninst
|
|
||||||
|
|
||||||
:: Remove the desktop and start menu icons
|
|
||||||
python pkgutils.py win-uninstall
|
|
||||||
|
|
||||||
:: Remove the PyQt5 and pyenchant dependencies
|
|
||||||
pip uninstall --yes -r requirements.txt
|
|
||||||
|
|
||||||
:afterUninst
|
|
||||||
|
|
||||||
echo.
|
|
||||||
pause
|
|
||||||
goto:eof
|
|
||||||
|
|
||||||
:errorNoPython
|
|
||||||
echo.
|
|
||||||
echo ERROR^: Python is not installed on your system, or cannot be found.
|
|
||||||
echo.
|
|
||||||
echo Please download and install it from https://www.python.org/downloads/
|
|
||||||
echo Also make sure the "Add Python to PATH" option is selected during installation.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
goto:eof
|
|
||||||
|
|
||||||
:errorNoSetup
|
|
||||||
echo.
|
|
||||||
echo ERROR^: Could not find the pkgutils.py script.
|
|
||||||
echo.
|
|
||||||
echo Make sure you run pkgutils.py from the novelWriter root folder.
|
|
||||||
echo.
|
|
||||||
|
|
||||||
pause
|
|
||||||
@@ -427,14 +427,7 @@ def testCoreTools_DocSearch(monkeypatch, mockGUI, fncPath, mockRnd, ipsumText):
|
|||||||
# ========
|
# ========
|
||||||
|
|
||||||
# Escape Using QRegularExpression
|
# Escape Using QRegularExpression
|
||||||
with monkeypatch.context() as mp:
|
assert search._buildPattern("[A-Za-z0-9_]+") == r"\[A\-Za\-z0\-9_\]\+"
|
||||||
mp.setattr(CONFIG, "verQtValue", 0x050f00)
|
|
||||||
assert search._buildPattern("[A-Za-z0-9_]+") == r"\[A\-Za\-z0\-9_\]\+"
|
|
||||||
|
|
||||||
# Escape Using Custom Implementation
|
|
||||||
with monkeypatch.context() as mp:
|
|
||||||
mp.setattr(CONFIG, "verQtValue", 0x050d00)
|
|
||||||
assert search._buildPattern("[A-Za-z0-9_]+") == r"\[A\-Za\-z0\-9_\]\+"
|
|
||||||
|
|
||||||
# Whole Words
|
# Whole Words
|
||||||
search.setWholeWords(True)
|
search.setWholeWords(True)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ from tools import C, buildTestProject
|
|||||||
|
|
||||||
from PyQt5.QtGui import QIcon
|
from PyQt5.QtGui import QIcon
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.constants import nwLists
|
from novelwriter.constants import nwLists
|
||||||
from novelwriter.core.item import NWItem
|
from novelwriter.core.item import NWItem
|
||||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||||
@@ -151,14 +151,13 @@ def testGuiViewerPanel_Tags(qtbot, monkeypatch, caplog, nwGUI, projPath, mockRnd
|
|||||||
nwGUI.docEditor._processTag(cursor, create=True)
|
nwGUI.docEditor._processTag(cursor, create=True)
|
||||||
|
|
||||||
# Check Panel Tab Visibility
|
# Check Panel Tab Visibility
|
||||||
if CONFIG.verQtValue >= 0x050f00:
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["CHARACTER"]) is True
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["CHARACTER"]) is True
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["PLOT"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["PLOT"]) is False
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["WORLD"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["WORLD"]) is False
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["TIMELINE"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["TIMELINE"]) is False
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["OBJECT"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["OBJECT"]) is False
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["ENTITY"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["ENTITY"]) is False
|
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["CUSTOM"]) is False
|
||||||
assert viewPanel.mainTabs.isTabVisible(viewPanel.idTabs["CUSTOM"]) is False
|
|
||||||
|
|
||||||
# Check Character Tab
|
# Check Character Tab
|
||||||
charTab = viewPanel.kwTabs["CHARACTER"]
|
charTab = viewPanel.kwTabs["CHARACTER"]
|
||||||
|
|||||||
Reference in New Issue
Block a user