manuskript/manuskript/main.py

196 lines
7.4 KiB
Python
Raw Normal View History

2015-05-28 13:32:09 +12:00
# -*- coding: utf-8 -*-
2016-02-07 00:34:22 +13:00
import faulthandler
import os
2015-05-28 13:32:09 +12:00
import sys
import re
2016-02-06 00:25:25 +13:00
import manuskript.ui.views.webView
from PyQt5.Qt import qVersion
2016-02-07 00:34:22 +13:00
from PyQt5.QtCore import QLocale, QTranslator, QSettings
2016-02-29 11:48:53 +13:00
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, qApp, QMessageBox
2016-02-29 11:48:53 +13:00
from manuskript.functions import appPath, writablePath
from manuskript.version import getVersion
2015-05-28 13:32:09 +12:00
faulthandler.enable()
2015-06-22 05:37:13 +12:00
def warnAboutBuggyLibraries(app):
"""Some bugs are out of our reach to fix. The user needs to be warned and perhaps take action themselves."""
# (Py)Qt 5.11 and 5.12 have a bug that can cause crashes when simply setting up
# various UI elements. This has been reported and verified to happen in the
# Export (Compile) screen, but due to the nature of the bug, we cannot be sure
# it won't cause random crashes in other parts of the application. (PR-612)
if re.match("^5\\.1[12](\\.?|$)", qVersion()):
warning1 = "The version of PyQt you are using ({}) is known to have a bug that can cause Manuskript to crash."
warning2 = "It is recommended that you upgrade to the latest version of PyQt."
# Don't translate for debug log.
print("WARNING:", warning1.format(qVersion()), warning2)
msg = QMessageBox(QMessageBox.Warning,
app.tr("You may experience crashes."),
"<p><b>" +
app.tr(warning1).format(qVersion()) +
"</b></p>" +
"<p>" +
app.tr(warning2) +
"</p>",
QMessageBox.Ignore | QMessageBox.Abort)
# Dialogs without a choice on them are just asking to be ignored...
# But with the option to 'Abort'...? Maybe someone will actually read it.
if msg.exec() == QMessageBox.Abort:
sys.exit(1)
2017-11-21 03:42:30 +13:00
def prepare(tests=False):
2015-05-28 13:32:09 +12:00
app = QApplication(sys.argv)
app.setOrganizationName("manuskript"+("_tests" if tests else ""))
2015-05-28 13:32:09 +12:00
app.setOrganizationDomain("www.theologeek.ch")
app.setApplicationName("manuskript"+("_tests" if tests else ""))
app.setApplicationVersion(getVersion())
2017-11-19 12:20:49 +13:00
print("Running manuskript version {}.".format(getVersion()))
2016-02-07 04:42:22 +13:00
icon = QIcon()
for i in [16, 32, 64, 128, 256, 512]:
2016-02-07 04:42:22 +13:00
icon.addFile(appPath("icons/Manuskript/icon-{}px.png".format(i)))
qApp.setWindowIcon(icon)
2015-06-07 00:21:48 +12:00
app.setStyle("Fusion")
2015-06-23 07:34:11 +12:00
# Load style from QSettings
settings = QSettings(app.organizationName(), app.applicationName())
if settings.contains("applicationStyle"):
style = settings.value("applicationStyle")
app.setStyle(style)
2016-02-06 00:25:25 +13:00
# Translation process
2015-06-08 12:10:18 +12:00
locale = QLocale.system().name()
2016-02-06 20:18:59 +13:00
appTranslator = QTranslator(app)
# By default: locale
def extractLocale(filename):
# len("manuskript_") = 13, len(".qm") = 3
return filename[11:-3] if len(filename) >= 16 else ""
def tryLoadTranslation(translation, source):
if appTranslator.load(appPath(os.path.join("i18n", translation))):
app.installTranslator(appTranslator)
print(app.tr("Loaded translation from {}: {}.").format(source, translation))
return True
else:
print(app.tr("Note: No translator found or loaded from {} for locale {}.").
format(source, extractLocale(translation)))
return False
# Load translation from settings
translation = ""
if settings.contains("applicationTranslation"):
translation = settings.value("applicationTranslation")
print("Found translation in settings:", translation)
if (translation != "" and not tryLoadTranslation(translation, "settings")) or translation == "":
# load from settings failed or not set, fallback
translation = "manuskript_{}.qm".format(locale)
tryLoadTranslation(translation, "system locale")
2016-02-06 00:25:25 +13:00
2015-09-29 22:17:03 +13:00
QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + [appPath("icons")])
QIcon.setThemeName("NumixMsk")
2015-06-23 07:34:11 +12:00
# Font siue
if settings.contains("appFontSize"):
f = qApp.font()
f.setPointSize(settings.value("appFontSize", type=int))
app.setFont(f)
2017-11-20 03:29:38 +13:00
# Main window
2017-11-19 12:20:49 +13:00
from manuskript.mainWindow import MainWindow
2015-06-23 07:34:11 +12:00
2017-11-20 03:29:38 +13:00
MW = MainWindow()
# We store the system default cursor flash time to be able to restore it
# later if necessary
2017-11-20 03:29:38 +13:00
MW._defaultCursorFlashTime = qApp.cursorFlashTime()
# Command line project
if len(sys.argv) > 1 and sys.argv[1][-4:] == ".msk":
if os.path.exists(sys.argv[1]):
path = os.path.abspath(sys.argv[1])
MW._autoLoadProject = path
2017-11-20 03:29:38 +13:00
return app, MW
def launch(app, MW = None):
warnAboutBuggyLibraries(app)
2017-11-20 03:29:38 +13:00
if MW is None:
from manuskript.functions import mainWindow
MW = mainWindow()
MW.show()
2015-06-23 07:34:11 +12:00
# Support for IPython Jupyter QT Console as a debugging aid.
# Last argument must be --console to enable it
# Code reference :
# https://github.com/ipython/ipykernel/blob/master/examples/embedding/ipkernel_qtapp.py
# https://github.com/ipython/ipykernel/blob/master/examples/embedding/internal_ipkernel.py
if len(sys.argv) > 1 and sys.argv[-1] == "--console":
try:
from IPython.lib.kernel import connect_qtconsole
from ipykernel.kernelapp import IPKernelApp
# Only to ensure matplotlib QT mainloop integration is available
import matplotlib
# Create IPython kernel within our application
kernel = IPKernelApp.instance()
# Initialize it and use matplotlib for main event loop integration with QT
kernel.initialize(['python', '--matplotlib=qt'])
# Create the console in a new process and connect
console = connect_qtconsole(kernel.abs_connection_file, profile=kernel.profile)
# Export MW and app variable to the console's namespace
kernel.shell.user_ns['MW'] = MW
kernel.shell.user_ns['app'] = app
kernel.shell.user_ns['kernel'] = kernel
kernel.shell.user_ns['console'] = console
# When we close manuskript, make sure we close the console process and stop the
# IPython kernel's mainloop, otherwise the app will never finish.
def console_cleanup():
app.quit()
console.kill()
kernel.io_loop.stop()
app.lastWindowClosed.connect(console_cleanup)
# Very important, IPython-specific step: this gets GUI event loop
# integration going, and it replaces calling app.exec_()
kernel.start()
except Exception as e:
print("Console mode requested but error initializing IPython : %s" % str(e))
print("To make use of the Interactive IPython QT Console, make sure you install : ")
print("$ pip3 install ipython qtconsole matplotlib")
qApp.exec_()
else:
qApp.exec_()
2015-06-22 04:35:42 +12:00
qApp.deleteLater()
2015-06-23 07:34:11 +12:00
2017-11-20 03:29:38 +13:00
def run():
"""
Run separates prepare and launch for two reasons:
1. I've read somewhere it helps with potential segfault (see comment below)
2. So that prepare can be used in tests, without running the whole thing
"""
# Need to return and keep `app` otherwise it gets deleted.
app, MW = prepare()
# Separating launch to avoid segfault, so it seem.
2017-11-20 03:29:38 +13:00
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
launch(app, MW)
2015-06-08 08:06:57 +12:00
if __name__ == "__main__":
2015-06-23 07:07:38 +12:00
run()