Update PyInstaller usage

Switch to rapidfuzz from thefuzz
Add associations to macOS app bundle
This commit is contained in:
Timmy Welch 2022-10-25 21:48:01 -07:00
parent 58904a927f
commit aad83c8c03
No known key found for this signature in database
38 changed files with 421 additions and 127 deletions

View File

@ -0,0 +1,7 @@
from __future__ import annotations
import os
def get_hook_dirs() -> list[str]:
return [os.path.dirname(__file__)]

View File

@ -0,0 +1,6 @@
from __future__ import annotations
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files("comicapi.data")

View File

@ -0,0 +1,5 @@
from __future__ import annotations
import pathlib
data_path = pathlib.Path(__file__).parent

View File

@ -26,7 +26,9 @@ from shutil import which # noqa: F401
from typing import Any
import pycountry
import thefuzz.fuzz
import rapidfuzz.fuzz
import comicapi.data
logger = logging.getLogger(__name__)
@ -155,7 +157,7 @@ def sanitize_title(text: str, basic: bool = False) -> str:
def titles_match(search_title: str, record_title: str, threshold: int = 90) -> bool:
sanitized_search = sanitize_title(search_title)
sanitized_record = sanitize_title(record_title)
ratio: int = thefuzz.fuzz.ratio(sanitized_search, sanitized_record)
ratio: int = rapidfuzz.fuzz.ratio(sanitized_search, sanitized_record)
logger.debug(
"search title: %s ; record title: %s ; ratio: %d ; match threshold: %d",
search_title,
@ -257,6 +259,6 @@ publishers: dict[str, ImprintDict] = {}
def load_publishers() -> None:
try:
update_publishers(json.loads((pathlib.Path(__file__).parent / "data" / "publishers.json").read_text("utf-8")))
update_publishers(json.loads((comicapi.data.data_path / "publishers.json").read_text("utf-8")))
except Exception:
logger.exception("Failed to load publishers.json; The are no publishers or imprints loaded")

View File

@ -1,51 +1,265 @@
# -*- mode: python -*-
# -*- mode: python ; coding: utf-8 -*-
import platform
from os.path import join
from comictaggerlib import ctversion
from PyInstaller.utils.hooks import get_module_file_attribute
enable_console = False
binaries = []
block_cipher = None
if platform.system() == "Windows":
a = Analysis(
["comictagger.py"],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
exe_binaries = []
exe_zipfiles = []
exe_datas = []
exe_exclude_binaries = True
coll_binaries = a.binaries
coll_zipfiles = a.zipfiles
coll_datas = a.datas
if platform.system() in ["Windows"]:
enable_console = True
exe_binaries = a.binaries
exe_zipfiles = a.zipfiles
exe_datas = a.datas
exe_exclude_binaries = False
a = Analysis(['comictagger.py'],
binaries=binaries,
datas=[('comictaggerlib/ui/*.ui', 'ui'), ('comictaggerlib/graphics', 'graphics'), ('comicapi/data', 'comicapi/data'),(os.path.join(os.path.dirname(get_module_file_attribute('wordninja')),"wordninja"), "wordninja")],
hiddenimports=['PIL'],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
a.binaries,
a.zipfiles,
a.datas,
# single file setup
exclude_binaries=False,
name='comictagger',
debug=False,
strip=False,
upx=True,
console=enable_console,
icon="windows/app.ico" )
coll_binaries = []
coll_zipfiles = []
coll_datas = []
app = BUNDLE(exe,
name='ComicTagger.app',
icon='mac/app.icns',
info_plist={
'NSHighResolutionCapable': 'True',
'NSRequiresAquaSystemAppearance': 'False',
'CFBundleDisplayName': 'ComicTagger',
'CFBundleShortVersionString': ctversion.version,
'CFBundleVersion': ctversion.version
},
bundle_identifier=None)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
exe_binaries,
exe_zipfiles,
exe_datas,
[],
exclude_binaries=exe_exclude_binaries,
name="comictagger",
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=enable_console,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon="windows/app.ico",
)
if platform.system() not in ["Windows"]:
coll = COLLECT(
exe,
coll_binaries,
coll_zipfiles,
coll_datas,
strip=False,
upx=True,
upx_exclude=[],
name="comictagger",
)
app = BUNDLE(
coll,
name="ComicTagger.app",
icon="mac/app.icns",
info_plist={
"NSHighResolutionCapable": "True",
"NSPrincipalClass": "NSApplication",
"NSRequiresAquaSystemAppearance": "False",
"CFBundleDisplayName": "ComicTagger",
"CFBundleShortVersionString": ctversion.version,
"CFBundleVersion": ctversion.version,
"CFBundleDocumentTypes": [
{
"CFBundleTypeRole": "Viewer",
"LSItemContentTypes": [
"com.rarlab.rar-archive",
],
"CFBundleTypeName": "RAR Archive",
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
"LSItemContentTypes": [
"public.folder",
],
"CFBundleTypeName": "Folder",
},
{
"CFBundleTypeExtensions": [
"cbz",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "ZIP Comic Archive",
# 'CFBundleTypeIconFile': 'cbz',
"LSItemContentTypes": [
"public.zip-comic-archive",
"com.simplecomic.cbz-archive",
"com.macitbetter.cbz-archive",
"public.cbz-archive",
"cx.c3.cbz-archive",
"com.yacreader.yacreader.cbz",
"com.milke.cbz-archive",
"com.bitcartel.comicbooklover.cbz",
"public.archive.cbz",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeExtensions": [
"cb7",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "7-Zip Comic Archive",
# 'CFBundleTypeIconFile': 'cb7',
"LSItemContentTypes": [
"com.simplecomic.cb7-archive",
"public.cb7-archive",
"com.macitbetter.cb7-archive",
"cx.c3.cb7-archive",
"org.7-zip.7-zip-comic-archive",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
{
"CFBundleTypeExtensions": [
"cbr",
],
"LSTypeIsPackage": False,
"NSPersistentStoreTypeKey": "Binary",
"CFBundleTypeIconSystemGenerated": True,
"CFBundleTypeName": "RAR Comic Archive",
# 'CFBundleTypeIconFile': 'cbr',
"LSItemContentTypes": [
"com.rarlab.rar-comic-archive",
"com.simplecomic.cbr-archive",
"com.macitbetter.cbr-archive",
"public.cbr-archive",
"cx.c3.cbr-archive",
"com.bitcartel.comicbooklover.cbr",
"com.milke.cbr-archive",
"public.archive.cbr",
"com.yacreader.yacreader.cbr",
],
"CFBundleTypeRole": "Editor",
"LSHandlerRank": "Default",
},
],
"UTImportedTypeDeclarations": [
{
"UTTypeIdentifier": "com.rarlab.rar-archive",
"UTTypeDescription": "RAR Archive",
"UTTypeConformsTo": [
"public.data",
"public.archive",
],
"UTTypeTagSpecification": {
"public.mime-type": [
"application/x-rar",
"application/x-rar-compressed",
],
"public.filename-extension": [
"rar",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cbr',
# 'UTTypeIconBackgroundName': comic-fill
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"com.rarlab.rar-archive",
],
# 'UTTypeIconFile': 'cbr',
"UTTypeIdentifier": "com.rarlab.rar-comic-archive",
"UTTypeDescription": "RAR Comic Archive",
"UTTypeTagSpecification": {
"public.mime-type": [
"application/vnd.comicbook-rar",
"application/x-cbr",
],
"public.filename-extension": [
"cbr",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cbz',
# 'UTTypeIconBackgroundName': 'comic-fill',
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"public.zip-archive",
],
# 'UTTypeIconFile': cbz,
"UTTypeIdentifier": "public.zip-comic-archive",
"UTTypeDescription": "ZIP Comic Archive",
"UTTypeTagSpecification": {
"public.filename-extension": [
"cbz",
],
},
},
{
# 'UTTypeIcons': {
# 'UTTypeIconText': 'cb7',
# 'UTTypeIconBackgroundName': comic-fill
# }
"UTTypeConformsTo": [
"public.data",
"public.archive",
"org.7-zip.7-zip-archive",
],
# 'UTTypeIconFile': cb7
"UTTypeIdentifier": "org.7-zip.7-zip-comic-archive",
"UTTypeDescription": "7-Zip Comic Archive",
"UTTypeTagSpecification": {
"public.mime-type": [
"application/vnd.comicbook+7-zip",
"application/x-cb7-compressed",
],
"public.filename-extension": [
"cb7",
],
},
},
],
},
bundle_identifier=None,
)

View File

@ -0,0 +1,11 @@
from __future__ import annotations
import os
import comicapi.__pyinstaller
def get_hook_dirs() -> list[str]:
hooks = [os.path.dirname(__file__)]
hooks.extend(comicapi.__pyinstaller.get_hook_dirs())
return hooks

View File

@ -0,0 +1,7 @@
from __future__ import annotations
from PyInstaller.utils.hooks import collect_data_files
datas = []
datas += collect_data_files("comictaggerlib.ui")
datas += collect_data_files("comictaggerlib.graphics")

View File

@ -0,0 +1,7 @@
from __future__ import annotations
import os
from PyInstaller.utils.hooks import get_module_file_attribute
datas = [(os.path.join(os.path.dirname(get_module_file_attribute("wordninja")), "wordninja"), "wordninja")]

View File

@ -4,7 +4,7 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -25,7 +25,7 @@ class QTextEditLogger(QtCore.QObject, logging.Handler):
class ApplicationLogWindow(QtWidgets.QDialog):
def __init__(self, log_handler: QTextEditLogger, parent: QtCore.QObject = None) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("logwindow.ui"), self)
uic.loadUi(ui_path / "logwindow.ui", self)
self.log_handler = log_handler
self.log_handler.qlog.connect(self.textEdit.append)

View File

@ -26,6 +26,7 @@ from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.resulttypes import IssueResult, MultipleMatch
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -44,7 +45,7 @@ class AutoTagMatchWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("matchselectionwindow.ui"), self)
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.settings = settings

View File

@ -20,7 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -30,7 +30,7 @@ class AutoTagProgressWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("autotagprogresswindow.ui"), self)
uic.loadUi(ui_path / "autotagprogresswindow.ui", self)
self.archiveCoverWidget = CoverImageWidget(self.archiveCoverContainer, CoverImageWidget.DataMode, False)
gridlayout = QtWidgets.QGridLayout(self.archiveCoverContainer)

View File

@ -20,6 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -28,7 +29,7 @@ class AutoTagStartWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("autotagstartwindow.ui"), self)
uic.loadUi(ui_path / "autotagstartwindow.ui", self)
self.label.setText(msg)
self.setWindowFlags(

View File

@ -30,6 +30,7 @@ from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.cbltransformer import CBLTransformer
from comictaggerlib.comicvinetalker import ComicVineTalker, ComicVineTalkerException
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
from comictaggerlib.graphics import graphics_path
from comictaggerlib.issueidentifier import IssueIdentifier
from comictaggerlib.resulttypes import IssueResult, MultipleMatch, OnlineMatchResults
from comictaggerlib.settings import ComicTaggerSettings
@ -214,7 +215,7 @@ def process_file_cli(
) -> None:
batch_mode = len(opts.file_list) > 1
ca = ComicArchive(filename, settings.rar_exe_path, ComicTaggerSettings.get_graphic("nocover.png"))
ca = ComicArchive(filename, settings.rar_exe_path, str(graphics_path / "nocover.png"))
if not os.path.lexists(filename):
logger.error("Cannot find %s", filename)

View File

@ -253,7 +253,7 @@ class ComicVineTalker:
# ORed together, and we get thousands of results. Good news is the
# results are sorted by relevance, so we can be smart about halting the search.
# 1. Don't fetch more than some sane amount of pages.
# 2. Halt when any result on the current page is less than or equal to a set ratio using thefuzz
# 2. Halt when any result on the current page is less than or equal to a set ratio using rapidfuzz
max_results = 500 # 5 pages
total_result_count = min(total_result_count, max_results)

View File

@ -27,10 +27,11 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi import utils
from comicapi.comicarchive import ComicArchive
from comictaggerlib.comicvinetalker import ComicVineTalker
from comictaggerlib.graphics import graphics_path
from comictaggerlib.imagefetcher import ImageFetcher
from comictaggerlib.imagepopup import ImagePopup
from comictaggerlib.pageloader import PageLoader
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import get_qimage_from_data, reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -91,7 +92,7 @@ class CoverImageWidget(QtWidgets.QWidget):
super().__init__(parent)
self.cover_fetcher = ImageFetcher()
uic.loadUi(ComicTaggerSettings.get_ui_file("coverimagewidget.ui"), self)
uic.loadUi(ui_path / "coverimagewidget.ui", self)
reduce_widget_font_size(self.label)
@ -115,8 +116,8 @@ class CoverImageWidget(QtWidgets.QWidget):
self.imageCount = 1
self.imageData = b""
self.btnLeft.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("left.png")))
self.btnRight.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("right.png")))
self.btnLeft.setIcon(QtGui.QIcon(str(graphics_path / "left.png")))
self.btnRight.setIcon(QtGui.QIcon(str(graphics_path / "right.png")))
self.btnLeft.clicked.connect(self.decrement_image)
self.btnRight.clicked.connect(self.increment_image)
@ -292,7 +293,7 @@ class CoverImageWidget(QtWidgets.QWidget):
self.page_loader = None
def load_default(self) -> None:
self.current_pixmap = QtGui.QPixmap(ComicTaggerSettings.get_graphic("nocover.png"))
self.current_pixmap = QtGui.QPixmap(str(graphics_path / "nocover.png"))
self.set_display_pixmap()
def resizeEvent(self, resize_event: QtGui.QResizeEvent) -> None:

View File

@ -20,7 +20,7 @@ from typing import Any
from PyQt5 import QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -32,7 +32,7 @@ class CreditEditorWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, mode: int, role: str, name: str, primary: bool) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("crediteditorwindow.ui"), self)
uic.loadUi(ui_path / "crediteditorwindow.ui", self)
self.mode = mode

View File

@ -20,6 +20,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -34,7 +35,7 @@ class ExportWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings, msg: str) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("exportwindow.ui"), self)
uic.loadUi(ui_path / "exportwindow.ui", self)
self.label.setText(msg)
self.setWindowFlags(

View File

@ -24,9 +24,11 @@ from PyQt5 import QtCore, QtWidgets, uic
from comicapi import utils
from comicapi.comicarchive import ComicArchive
from comictaggerlib.graphics import graphics_path
from comictaggerlib.optionalmsgdialog import OptionalMessageDialog
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import linuxRarHelp, macRarHelp, windowsRarHelp
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent, reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -62,7 +64,7 @@ class FileSelectionList(QtWidgets.QWidget):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("fileselectionlist.ui"), self)
uic.loadUi(ui_path / "fileselectionlist.ui", self)
self.settings = settings
@ -279,7 +281,7 @@ class FileSelectionList(QtWidgets.QWidget):
if self.is_list_dupe(path):
return self.get_current_list_row(path)
ca = ComicArchive(path, self.settings.rar_exe_path, ComicTaggerSettings.get_graphic("nocover.png"))
ca = ComicArchive(path, self.settings.rar_exe_path, str(graphics_path / "nocover.png"))
if ca.seems_to_be_a_comic_archive():
row: int = self.twList.rowCount()

View File

@ -0,0 +1,5 @@
from __future__ import annotations
import pathlib
graphics_path = pathlib.Path(__file__).parent

View File

@ -19,7 +19,8 @@ import logging
from PyQt5 import QtCore, QtGui, QtWidgets, sip, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -28,7 +29,7 @@ class ImagePopup(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, image_pixmap: QtGui.QPixmap) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("imagepopup.ui"), self)
uic.loadUi(ui_path / "imagepopup.ui", self)
QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.CursorShape.WaitCursor))
@ -47,7 +48,7 @@ class ImagePopup(QtWidgets.QDialog):
# TODO: macOS denies this
screen = QtWidgets.QApplication.primaryScreen()
self.desktopBg = screen.grabWindow(sip.voidptr(0), 0, 0, screen_size.width(), screen_size.height())
bg = QtGui.QPixmap(ComicTaggerSettings.get_graphic("popup_bg.png"))
bg = QtGui.QPixmap(str(graphics_path / "popup_bg.png"))
self.clientBgPixmap = bg.scaled(
screen_size.width(),
screen_size.height(),

View File

@ -24,6 +24,7 @@ from comictaggerlib.comicvinetalker import ComicVineTalker, ComicVineTalkerExcep
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.resulttypes import CVIssuesResults
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -45,7 +46,7 @@ class IssueSelectionWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("issueselectionwindow.ui"), self)
uic.loadUi(ui_path / "issueselectionwindow.ui", self)
self.coverWidget = CoverImageWidget(self.coverImageContainer, CoverImageWidget.AltCoverMode)
gridlayout = QtWidgets.QGridLayout(self.coverImageContainer)

View File

@ -19,8 +19,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import qtutils
from comictaggerlib.ui import qtutils, ui_path
logger = logging.getLogger(__name__)
@ -29,7 +28,7 @@ class LogWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("logwindow.ui"), self)
uic.loadUi(ui_path / "logwindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(

View File

@ -29,6 +29,7 @@ from comicapi import utils
from comictaggerlib import cli
from comictaggerlib.comicvinetalker import ComicVineTalker
from comictaggerlib.ctversion import version
from comictaggerlib.graphics import graphics_path
from comictaggerlib.options import parse_cmd_line
from comictaggerlib.settings import ComicTaggerSettings
@ -89,6 +90,17 @@ try:
qt_exception_hook = UncaughtHook()
from comictaggerlib.taggerwindow import TaggerWindow
class Application(QtWidgets.QApplication):
openFileRequest = QtCore.pyqtSignal(QtCore.QUrl, name="openfileRequest")
def event(self, event):
if event.type() == QtCore.QEvent.FileOpen:
logger.info(event.url().toLocalFile())
self.openFileRequest.emit(event.url())
return True
return super().event(event)
except ImportError as e:
def show_exception_box(log_msg: str) -> None:
@ -179,10 +191,14 @@ def ctmain() -> None:
if opts.darkmode:
args.extend(["-platform", "windows:darkmode=2"])
args.extend(sys.argv)
app = QtWidgets.QApplication(args)
app = Application(args)
# needed to catch initial open file events (macOS)
app.openFileRequest.connect(lambda x: opts.files.append(x.toLocalFile()))
if platform.system() == "Darwin":
# Set the MacOS dock icon
app.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
app.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
if platform.system() == "Windows":
# For pure python, tell windows that we're not python,
@ -198,7 +214,7 @@ def ctmain() -> None:
ctypes.windll.user32.SetWindowPos(console_wnd, None, 0, 0, 0, 0, swp_hidewindow) # type: ignore[attr-defined]
if platform.system() != "Linux":
img = QtGui.QPixmap(ComicTaggerSettings.get_graphic("tags.png"))
img = QtGui.QPixmap(str(graphics_path / "tags.png"))
splash = QtWidgets.QSplashScreen(img)
splash.show()
@ -207,9 +223,12 @@ def ctmain() -> None:
try:
tagger_window = TaggerWindow(opts.files, settings, opts=opts)
tagger_window.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
tagger_window.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
tagger_window.show()
# Catch open file events (macOS)
app.openFileRequest.connect(tagger_window.open_file_event)
if platform.system() != "Linux":
splash.finish(tagger_window)

View File

@ -23,7 +23,7 @@ from PyQt5 import QtCore, QtWidgets, uic
from comicapi.comicarchive import ComicArchive
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.resulttypes import IssueResult
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -35,7 +35,7 @@ class MatchSelectionWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, matches: list[IssueResult], comic_archive: ComicArchive) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("matchselectionwindow.ui"), self)
uic.loadUi(ui_path / "matchselectionwindow.ui", self)
self.altCoverWidget = CoverImageWidget(self.altCoverContainer, CoverImageWidget.AltCoverMode)
gridlayout = QtWidgets.QGridLayout(self.altCoverContainer)

View File

@ -23,7 +23,8 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi.comicarchive import ComicArchive
from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -32,7 +33,7 @@ class PageBrowserWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, metadata: GenericMetadata) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("pagebrowser.ui"), self)
uic.loadUi(ui_path / "pagebrowser.ui", self)
self.pageWidget = CoverImageWidget(self.pageContainer, CoverImageWidget.ArchiveMode)
gridlayout = QtWidgets.QGridLayout(self.pageContainer)
@ -58,8 +59,8 @@ class PageBrowserWindow(QtWidgets.QDialog):
self.btnPrev.setText("<<")
self.btnNext.setText(">>")
else:
self.btnPrev.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("left.png")))
self.btnNext.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("right.png")))
self.btnPrev.setIcon(QtGui.QIcon(str(graphics_path / "left.png")))
self.btnNext.setIcon(QtGui.QIcon(str(graphics_path / "right.png")))
self.btnNext.clicked.connect(self.next_page)
self.btnPrev.clicked.connect(self.prev_page)

View File

@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets, uic
from comicapi.comicarchive import ComicArchive, MetaDataStyle
from comicapi.genericmetadata import ImageMetadata, PageType
from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -70,7 +70,7 @@ class PageListEditor(QtWidgets.QWidget):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("pagelisteditor.ui"), self)
uic.loadUi(ui_path / "pagelisteditor.ui", self)
self.pageWidget = CoverImageWidget(self.pageContainer, CoverImageWidget.ArchiveMode)
gridlayout = QtWidgets.QGridLayout(self.pageContainer)

View File

@ -19,7 +19,7 @@ import logging
from PyQt5 import QtCore, QtWidgets, uic
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -29,7 +29,7 @@ class IDProgressWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("progresswindow.ui"), self)
uic.loadUi(ui_path / "progresswindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(

View File

@ -25,6 +25,7 @@ from comicapi.genericmetadata import GenericMetadata
from comictaggerlib.filerenamer import FileRenamer, get_rename_dir
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import SettingsWindow
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent
logger = logging.getLogger(__name__)
@ -40,7 +41,7 @@ class RenameWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("renamewindow.ui"), self)
uic.loadUi(ui_path / "renamewindow.ui", self)
self.label.setText(f"Preview (based on {MetaDataStyle.name[data_style]} tags):")
self.setWindowFlags(
@ -57,7 +58,8 @@ class RenameWindow(QtWidgets.QDialog):
self.rename_list: list[str] = []
self.btnSettings.clicked.connect(self.modify_settings)
self.renamer = FileRenamer(None, platform="universal" if self.settings.rename_strict else "auto")
platform = "universal" if self.settings.rename_strict else "auto"
self.renamer = FileRenamer(None, platform=platform)
self.do_preview()

View File

@ -20,7 +20,6 @@ import logging
import os
import pathlib
import platform
import sys
import uuid
from collections.abc import Iterator
from typing import TextIO, no_type_check
@ -42,21 +41,6 @@ class ComicTaggerSettings:
ComicTaggerSettings.folder = pathlib.Path(os.path.expanduser("~")) / ".ComicTagger"
return pathlib.Path(ComicTaggerSettings.folder)
@staticmethod
def base_dir() -> pathlib.Path:
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
return pathlib.Path(sys._MEIPASS) # type: ignore[attr-defined]
return pathlib.Path(__file__).parent
@staticmethod
def get_graphic(filename: str | pathlib.Path) -> str:
return str(ComicTaggerSettings.base_dir() / "graphics" / filename)
@staticmethod
def get_ui_file(filename: str | pathlib.Path) -> pathlib.Path:
return ComicTaggerSettings.base_dir() / "ui" / filename
def __init__(self, folder: str | pathlib.Path | None) -> None:
# General Settings
self.rar_exe_path = ""

View File

@ -30,6 +30,7 @@ from comictaggerlib.comicvinetalker import ComicVineTalker
from comictaggerlib.filerenamer import FileRenamer
from comictaggerlib.imagefetcher import ImageFetcher
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
logger = logging.getLogger(__name__)
@ -130,7 +131,7 @@ class SettingsWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget, settings: ComicTaggerSettings) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("settingswindow.ui"), self)
uic.loadUi(ui_path / "settingswindow.ui", self)
self.setWindowFlags(
QtCore.Qt.WindowType(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
@ -407,4 +408,4 @@ class TemplateHelpWindow(QtWidgets.QDialog):
def __init__(self, parent: QtWidgets.QWidget) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("TemplateHelp.ui"), self)
uic.loadUi(ui_path / "TemplateHelp.ui", self)

View File

@ -50,6 +50,7 @@ from comictaggerlib.coverimagewidget import CoverImageWidget
from comictaggerlib.crediteditorwindow import CreditEditorWindow
from comictaggerlib.exportwindow import ExportConflictOpts, ExportWindow
from comictaggerlib.fileselectionlist import FileInfo, FileSelectionList
from comictaggerlib.graphics import graphics_path
from comictaggerlib.issueidentifier import IssueIdentifier
from comictaggerlib.logwindow import LogWindow
from comictaggerlib.optionalmsgdialog import OptionalMessageDialog
@ -59,6 +60,7 @@ from comictaggerlib.renamewindow import RenameWindow
from comictaggerlib.resulttypes import IssueResult, MultipleMatch, OnlineMatchResults
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.settingswindow import SettingsWindow
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import center_window_on_parent, reduce_widget_font_size
from comictaggerlib.versionchecker import VersionChecker
from comictaggerlib.volumeselectionwindow import VolumeSelectionWindow
@ -83,7 +85,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("taggerwindow.ui"), self)
uic.loadUi(ui_path / "taggerwindow.ui", self)
self.settings = settings
self.log_window = self.setup_logger()
@ -150,7 +152,7 @@ class TaggerWindow(QtWidgets.QMainWindow):
self.scrollAreaWidgetContents.adjustSize()
self.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
# TODO: this needs to be looked at
if opts is not None and opts.type:
# respect the command line option tag type
@ -261,6 +263,10 @@ Have fun!
if self.settings.check_for_new_version:
pass
def open_file_event(self, url: QtCore.QUrl) -> None:
logger.info(url.toLocalFile())
self.fileSelectionList.add_path_list([url.toLocalFile()])
def sigint_handler(self, *args: Any) -> None:
# defer the actual close in the app loop thread
QtCore.QTimer.singleShot(200, lambda: execute(self.close))
@ -297,7 +303,7 @@ Have fun!
def update_app_title(self) -> None:
self.setWindowIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("app.png")))
self.setWindowIcon(QtGui.QIcon(str(graphics_path / "app.png")))
if self.comic_archive is None:
self.setWindowTitle(self.appName)
@ -418,21 +424,21 @@ Have fun!
self.actionComicTaggerForum.triggered.connect(self.show_forum)
# Notes Menu
self.btnOpenWebLink.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("open.png")))
self.btnOpenWebLink.setIcon(QtGui.QIcon(str(graphics_path / "open.png")))
# ToolBar
self.actionLoad.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("open.png")))
self.actionLoadFolder.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("longbox.png")))
self.actionWrite_Tags.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("save.png")))
self.actionParse_Filename.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("parse.png")))
self.actionParse_Filename_split_words.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("parse.png")))
self.actionSearchOnline.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("search.png")))
self.actionLiteralSearch.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("search.png")))
self.actionAutoIdentify.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("auto.png")))
self.actionAutoTag.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("autotag.png")))
self.actionAutoImprint.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("autotag.png")))
self.actionClearEntryForm.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("clear.png")))
self.actionPageBrowser.setIcon(QtGui.QIcon(ComicTaggerSettings.get_graphic("browse.png")))
self.actionLoad.setIcon(QtGui.QIcon(str(graphics_path / "open.png")))
self.actionLoadFolder.setIcon(QtGui.QIcon(str(graphics_path / "longbox.png")))
self.actionWrite_Tags.setIcon(QtGui.QIcon(str(graphics_path / "save.png")))
self.actionParse_Filename.setIcon(QtGui.QIcon(str(graphics_path / "parse.png")))
self.actionParse_Filename_split_words.setIcon(QtGui.QIcon(str(graphics_path / "parse.png")))
self.actionSearchOnline.setIcon(QtGui.QIcon(str(graphics_path / "search.png")))
self.actionLiteralSearch.setIcon(QtGui.QIcon(str(graphics_path / "search.png")))
self.actionAutoIdentify.setIcon(QtGui.QIcon(str(graphics_path / "auto.png")))
self.actionAutoTag.setIcon(QtGui.QIcon(str(graphics_path / "autotag.png")))
self.actionAutoImprint.setIcon(QtGui.QIcon(str(graphics_path / "autotag.png")))
self.actionClearEntryForm.setIcon(QtGui.QIcon(str(graphics_path / "clear.png")))
self.actionPageBrowser.setIcon(QtGui.QIcon(str(graphics_path / "browse.png")))
self.toolBar.addAction(self.actionLoad)
self.toolBar.addAction(self.actionLoadFolder)
@ -567,7 +573,7 @@ Have fun!
msg_box = QtWidgets.QMessageBox()
msg_box.setWindowTitle("About " + self.appName)
msg_box.setTextFormat(QtCore.Qt.TextFormat.RichText)
msg_box.setIconPixmap(QtGui.QPixmap(ComicTaggerSettings.get_graphic("about.png")))
msg_box.setIconPixmap(QtGui.QPixmap(str(graphics_path / "about.png")))
msg_box.setText(
"<br><br><br>"
+ self.appName

View File

@ -1 +1,5 @@
from __future__ import annotations
import pathlib
ui_path = pathlib.Path(__file__).parent

View File

@ -6,7 +6,7 @@ import io
import logging
import traceback
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.graphics import graphics_path
logger = logging.getLogger(__name__)
@ -77,7 +77,7 @@ if qt_available:
pass
# if still nothing, go with default image
if not success:
img.load(ComicTaggerSettings.get_graphic("nocover.png"))
img.load(str(graphics_path / "nocover.png"))
return img
def qt_error(msg: str, e: Exception | None = None) -> None:

View File

@ -33,6 +33,7 @@ from comictaggerlib.matchselectionwindow import MatchSelectionWindow
from comictaggerlib.progresswindow import IDProgressWindow
from comictaggerlib.resulttypes import CVVolumeResults
from comictaggerlib.settings import ComicTaggerSettings
from comictaggerlib.ui import ui_path
from comictaggerlib.ui.qtutils import reduce_widget_font_size
logger = logging.getLogger(__name__)
@ -109,7 +110,7 @@ class VolumeSelectionWindow(QtWidgets.QDialog):
) -> None:
super().__init__(parent)
uic.loadUi(ComicTaggerSettings.get_ui_file("volumeselectionwindow.ui"), self)
uic.loadUi(ui_path / "volumeselectionwindow.ui", self)
self.imageWidget = CoverImageWidget(self.imageContainer, CoverImageWidget.URLMode)
gridlayout = QtWidgets.QGridLayout(self.imageContainer)

View File

@ -1 +0,0 @@
thefuzz[speedup]>=0.19.0

View File

@ -6,8 +6,8 @@ pillow>=9.1.0
py7zr
pycountry
pyicu; sys_platform == 'linux' or sys_platform == 'darwin'
rapidfuzz>=2.12.0
requests==2.*
text2digits
thefuzz>=0.19.0
typing_extensions
wordninja

View File

@ -5,7 +5,7 @@ flake8-black
flake8-encodings
flake8-isort
isort>=5.10
pyinstaller>=4.10
pyinstaller>=4.10, != 5.6
pytest==7.*
setuptools>=42
setuptools_scm[toml]>=3.4

View File

@ -57,7 +57,12 @@ setup(
url="https://github.com/comictagger/comictagger",
packages=["comictaggerlib", "comicapi"],
package_data={"comictaggerlib": ["ui/*", "graphics/*"], "comicapi": ["data/*"]},
entry_points=dict(console_scripts=["comictagger=comictaggerlib.main:ctmain"]),
entry_points=dict(
console_scripts=["comictagger=comictaggerlib.main:ctmain"],
pyinstaller40=[
"hook-dirs = comictaggerlib.__pyinstaller:get_hook_dirs",
],
),
classifiers=[
"Development Status :: 4 - Beta",
"Environment :: Console",