class Application(QObject): """Class to implement a Qt Application.""" instance = None _logger = _module_logger.getChild('Application') ############################################## # Fixme: Singleton @classmethod def create(cls, *args, **kwargs): if cls.instance is not None: raise NameError('Instance exists') cls.instance = cls(*args, **kwargs) return cls.instance ############################################## def __init__(self): super().__init__() QtCore.qInstallMessageHandler(self._message_handler) self._parse_arguments() self._appplication = QGuiApplication(sys.argv) self._engine = QQmlApplicationEngine() self._qml_application = QmlApplication(self) logo_path = ':/icons/logo-256.png' self._appplication.setWindowIcon(QIcon(logo_path)) self._platform = QtPlatform() # self._logger.info('\n' + str(self._platform)) self._scene = None # self._load_translation() self._register_qml_types() self._set_context_properties() self._load_qml_main() # self._run_before_event_loop() QTimer.singleShot(0, self._post_init) # self._view = QQuickView() # self._view.setResizeMode(QQuickView.SizeRootObjectToView) # self._view.setSource(qml_url) ############################################## @property def args(self): return self._args @property def qml_application(self): return self._qml_application @property def platform(self): return self._platform ############################################## def _print_critical_message(self, message): print('\nCritical Error on {}'.format(datetime.datetime.now())) print('-' * 80) print(message) # Fixme: don't print ??? # self._logger.critical(message) # Fixme: useless sys.stdout.flush() sys.stderr.flush() ############################################## def _message_handler(self, msg_type, context, msg): if msg_type == QtCore.QtDebugMsg: method = self._logger.debug elif msg_type == QtCore.QtInfoMsg: method = self._logger.info elif msg_type == QtCore.QtWarningMsg: method = self._logger.warning # elif msg_type == QtCore.QtCriticalMsg: # method = self._logger.critical # elif msg_type == QtCore.QtFatalMsg: # method = self._logger.critical elif msg_type in (QtCore.QtCriticalMsg, QtCore.QtFatalMsg): # method = self._logger.critical method = None # local_msg = msg.toLocal8Bit() # localMsg.constData() context_file = context.file if context_file is not None: file_path = Path(context_file).name else: file_path = '' message = '{1} {3} — {0}'.format(msg, file_path, context.line, context.function) if method is not None: method(message) else: self._print_critical_message(message) ############################################## def _on_critical_exception(self, exception): message = str(exception) + '\n' + traceback.format_exc() self._print_critical_message(message) sys.exit(1) ############################################## @classmethod def setup_gui_application(cls): # QGuiApplication.setApplicationName(APPLICATION_NAME) # QGuiApplication.setOrganizationName(ORGANISATION_NAME) QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling) # QQuickStyle.setStyle('Material') ############################################## def _parse_arguments(self): parser = argparse.ArgumentParser(description='Patro', ) # parser.add_argument( # '--version', # action='store_true', default=False, # help="show version and exit", # ) parser.add_argument( '--user-script', action=PathAction, default=None, help='user script to execute', ) parser.add_argument( '--user-script-args', default='', help="user script args (don't forget to quote)", ) self._args = parser.parse_args() ############################################## # def _load_translationt(self): # locale = QLocale() # if m_translator.load(locale, '...', '.', ':/translations', '.qm'): # m_application.installTranslator(m_translator) # else: # raise "No translator for locale" locale.name() ############################################## def _register_qml_types(self): qmlRegisterUncreatableType(QmlApplication, 'Patro', 1, 0, 'QmlApplication', 'Cannot create QmlApplication') qmlRegisterUncreatableType(QtScene, 'Patro', 1, 0, 'QtScene', 'Cannot create QtScene') # qmlRegisterType(QmlApplication, 'Patro', 1, 0, 'QmlApplication') # qmlRegisterType(QtScene, 'Patro', 1, 0, 'QtScene') qmlRegisterType(QtQuickPaintedSceneItem, 'Patro', 1, 0, 'PaintedSceneItem') ############################################## def _set_context_properties(self): context = self._engine.rootContext() context.setContextProperty('application', self._qml_application) ############################################## def _load_qml_main(self): # self._engine.addImportPath('qrc:///qml') qml_path = Path(__file__).parent.joinpath('qml', 'main.qml') self._qml_url = QUrl.fromLocalFile(str(qml_path)) # QUrl('qrc:/qml/main.qml') self._engine.objectCreated.connect(self._check_qml_is_loaded) self._engine.load(self._qml_url) ############################################## def _check_qml_is_loaded(self, obj, url): # See https://bugreports.qt.io/browse/QTBUG-39469 if (obj is None and url == self._qml_url): sys.exit(-1) ############################################## def exec_(self): # self._view.show() sys.exit(self._appplication.exec_()) ############################################## def _post_init(self): # Fixme: ui refresh ??? self._logger.info('post init') if self._args.user_script is not None: self.execute_user_script(self._args.user_script) ############################################## def execute_user_script(self, script_path): """Execute an user script provided by file *script_path* in a context where is defined a variable *application* that is a reference to the application instance. """ script_path = Path(script_path).absolute() self._logger.info('Execute user script:\n {}'.format(script_path)) try: source = open(script_path).read() except FileNotFoundError: self._logger.info('File {} not found'.format(script_path)) sys.exit(1) try: bytecode = compile(source, script_path, 'exec') except SyntaxError as exception: self._on_critical_exception(exception) try: exec(bytecode, {'application': self}) except Exception as exception: self._on_critical_exception(exception) self._logger.info('User script done')
class Application(QObject): """Class to implement a Qt Application.""" instance = None _logger = _module_logger.getChild('Application') scanner_ready = Signal() ############################################## # Fixme: Singleton @classmethod def create(cls, *args, **kwargs): if cls.instance is not None: raise NameError('Instance exists') cls.instance = cls(*args, **kwargs) return cls.instance ############################################## def __init__(self): self._logger.info('Ctor') super().__init__() QtCore.qInstallMessageHandler(self._message_handler) self._parse_arguments() self._library = None self._book = None # Fixme: must be defined before QML if BookLibrary.is_library(self._args.path): self.load_library(self._args.path) else: self.load_book(self._args.path) # For Qt Labs Platform native widgets # self._application = QGuiApplication(sys.argv) # use QCoreApplication::instance() to get instance self._application = QApplication(sys.argv) self._application.main = self self._init_application() self._engine = QQmlApplicationEngine() self._qml_application = QmlApplication(self) self._application.qml_main = self._qml_application self._platform = QtPlatform() # self._logger.info('\n' + str(self._platform)) self._load_translation() self._register_qml_types() self._set_context_properties() self._load_qml_main() # self._run_before_event_loop() self._thread_pool = QtCore.QThreadPool() self._logger.info("Multithreading with maximum {} threads".format( self._thread_pool.maxThreadCount())) self._scanner = None self._scanner_image_provider = ScannerImageProvider() self._engine.addImageProvider('scanner_image', self._scanner_image_provider) QTimer.singleShot(0, self._post_init) # self._view = QQuickView() # self._view.setResizeMode(QQuickView.SizeRootObjectToView) # self._view.setSource(qml_url) ############################################## @property def args(self): return self._args @property def platform(self): return self._platform @property def settings(self): return self._settings @property def qml_application(self): return self._qml_application @property def thread_pool(self): return self._thread_pool @property def scanner_image_provider(self): return self._scanner_image_provider @property def scanner(self): return self.init_scanner() @property def book(self): return self._book # @property # def book_path(self): # return self._book.path @property def library(self): return self._library ############################################## def _print_critical_message(self, message): # print('\nCritical Error on {}'.format(datetime.datetime.now())) # print('-'*80) # print(message) self._logger.critical(message) ############################################## def _message_handler(self, msg_type, context, msg): if msg_type == QtCore.QtDebugMsg: method = self._logger.debug elif msg_type == QtCore.QtInfoMsg: method = self._logger.info elif msg_type == QtCore.QtWarningMsg: method = self._logger.warning elif msg_type in (QtCore.QtCriticalMsg, QtCore.QtFatalMsg): method = self._logger.critical # method = None # local_msg = msg.toLocal8Bit() # localMsg.constData() context_file = context.file if context_file is not None: file_path = Path(context_file).name else: file_path = '' message = '{1} {3} — {0}'.format(msg, file_path, context.line, context.function) if method is not None: method(message) else: self._print_critical_message(message) ############################################## def _on_critical_exception(self, exception): message = str(exception) + '\n' + traceback.format_exc() self._print_critical_message(message) self._qml_application.notify_error(exception) # sys.exit(1) ############################################## def _init_application(self): self._application.setOrganizationName( ApplicationMetadata.organisation_name) self._application.setOrganizationDomain( ApplicationMetadata.organisation_domain) self._application.setApplicationName(ApplicationMetadata.name) self._application.setApplicationDisplayName( ApplicationMetadata.display_name) self._application.setApplicationVersion(ApplicationMetadata.version) logo_path = ':/icons/logo/logo-256.png' self._application.setWindowIcon(QIcon(logo_path)) QIcon.setThemeName('material') self._settings = ApplicationSettings() ############################################## @classmethod def setup_gui_application(self): # https://bugreports.qt.io/browse/QTBUG-55167 # for path in ( # 'qt.qpa.xcb.xcberror', # ): # QtCore.QLoggingCategory.setFilterRules('{} = false'.format(path)) QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling) # QQuickStyle.setStyle('Material') ############################################## def _parse_arguments(self): parser = argparse.ArgumentParser(description='BookBrowser', ) # parser.add_argument( # '--version', # action='store_true', default=False, # help="show version and exit", # ) # Fixme: should be able to start application without !!! parser.add_argument( 'path', metavar='PATH', action=PathAction, help='book or library path', ) parser.add_argument( '--dont-translate', action='store_true', default=False, help="Don't translate application", ) parser.add_argument( '--watcher', action='store_true', default=False, help='start watcher', ) parser.add_argument( '--fake-scanner', action='store_true', default=False, help='use a fake scanner', ) parser.add_argument( '--user-script', action=PathAction, default=None, help='user script to execute', ) parser.add_argument( '--user-script-args', default='', help="user script args (don't forget to quote)", ) self._args = parser.parse_args() ############################################## def _load_translation(self): if self._args.dont_translate: return # Fixme: ConfigInstall # directory = ':/translations' directory = str(Path(__file__).parent.joinpath('rcc', 'translations')) locale = QtCore.QLocale() self._translator = QtCore.QTranslator() if self._translator.load(locale, 'book-browser', '.', directory, '.qm'): self._application.installTranslator(self._translator) else: raise NameError('No translator for locale {}'.format( locale.name())) ############################################## def _register_qml_types(self): qmlRegisterType(KeySequenceEditor, 'BookBrowser', 1, 0, 'KeySequenceEditor') qmlRegisterUncreatableType(Shortcut, 'BookBrowser', 1, 0, 'Shortcut', 'Cannot create Shortcut') qmlRegisterUncreatableType(ApplicationSettings, 'BookBrowser', 1, 0, 'ApplicationSettings', 'Cannot create ApplicationSettings') qmlRegisterUncreatableType(QmlApplication, 'BookBrowser', 1, 0, 'QmlApplication', 'Cannot create QmlApplication') qmlRegisterUncreatableType(QmlBookCover, 'BookBrowser', 1, 0, 'QmlBookCover', 'Cannot create QmlBookCover') qmlRegisterUncreatableType(QmlBookLibrary, 'BookBrowser', 1, 0, 'QmlBookLibrary', 'Cannot create QmlBookLi') qmlRegisterUncreatableType(QmlBook, 'BookBrowser', 1, 0, 'QmlBook', 'Cannot create QmlBook') qmlRegisterUncreatableType(QmlBookPage, 'BookBrowser', 1, 0, 'QmlBookPage', 'Cannot create QmlBookPage') qmlRegisterUncreatableType(QmlBookMetadata, 'BookBrowser', 1, 0, 'QmlBookMetadata', 'Cannot create QmlBookMetadata') qmlRegisterUncreatableType(QmlScannerConfig, 'BookBrowser', 1, 0, 'QmlScannerConfig', 'Cannot create QmlScannerConfig') qmlRegisterUncreatableType(QmlScanner, 'BookBrowser', 1, 0, 'QmlScanner', 'Cannot create QmlScanner') ############################################## def _set_context_properties(self): context = self._engine.rootContext() context.setContextProperty('application', self._qml_application) context.setContextProperty('application_settings', self._settings) ############################################## def _load_qml_main(self): self._logger.info('Load QML...') qml_path = Path(__file__).parent.joinpath('qml') # qml_path = 'qrc:///qml' self._engine.addImportPath(str(qml_path)) main_qml_path = qml_path.joinpath('main.qml') self._qml_url = QUrl.fromLocalFile(str(main_qml_path)) # QUrl('qrc:/qml/main.qml') self._engine.objectCreated.connect(self._check_qml_is_loaded) self._engine.load(self._qml_url) self._logger.info('QML loaded') ############################################## def _check_qml_is_loaded(self, obj, url): # See https://bugreports.qt.io/browse/QTBUG-39469 if (obj is None and url == self._qml_url): sys.exit(-1) ############################################## def exec_(self): # self._view.show() self._logger.info('Start event loop') sys.exit(self._application.exec_()) ############################################## def _post_init(self): # Fixme: ui refresh ??? self._logger.info('post Init...') if self._args.watcher: self._logger.info('Start watcher') self._book.start_watcher() # QtCore.QFileSystemWatcher(self) if self._args.user_script is not None: self.execute_user_script(self._args.user_script) self._logger.info('Post Init Done') ############################################## def execute_user_script(self, script_path): """Execute an user script provided by file *script_path* in a context where is defined a variable *application* that is a reference to the application instance. """ script_path = Path(script_path).absolute() self._logger.info('Execute user script:\n {}'.format(script_path)) try: source = open(script_path).read() except FileNotFoundError: self._logger.info('File {} not found'.format(script_path)) sys.exit(1) try: bytecode = compile(source, script_path, 'exec') except SyntaxError as exception: self._on_critical_exception(exception) try: exec(bytecode, {'application': self}) except Exception as exception: self._on_critical_exception(exception) self._logger.info('User script done') ############################################## def init_scanner(self): if self._scanner is None: # take time self._scanner = QmlScanner(fake=self._args.fake_scanner) self.scanner_ready.emit() return self._scanner ############################################## def load_book(self, path): self._logger.info('Load book {} ...'.format(path)) self._book = QmlBook(path) self._logger.info('Book loaded') ############################################## def load_library(self, path): self._logger.info('Load library {} ...'.format(path)) self._library = QmlBookLibrary(path) self._logger.info('library loaded')
class Application(QObject): """Class to implement a Qt Application.""" instance = None QmlApplication_CLS = QmlApplication _logger = _module_logger.getChild('Application') ############################################## # Fixme: Singleton @classmethod def create(cls, *args, **kwargs): if cls.instance is not None: raise NameError('Instance exists') cls.instance = cls(*args, **kwargs) return cls.instance ############################################## def __init__(self): self._logger.info('Ctor') super().__init__() QtCore.qInstallMessageHandler(self._message_handler) self._parse_arguments() # For Qt Labs Platform native widgets # self._application = QGuiApplication(sys.argv) # use QCoreApplication::instance() to get instance self._application = QApplication(sys.argv) self._application.main = self self._init_application() self._engine = QQmlApplicationEngine() self._qml_application = self.QmlApplication_CLS(self) self._application.qml_main = self._qml_application self._platform = QtPlatform() # self._logger.info('\n' + str(self._platform)) #! self._load_translation() self.register_qml_types() self._set_context_properties() self._load_qml_main() # self._run_before_event_loop() self._application.aboutToQuit.connect(self.aboutToQuit) QTimer.singleShot(0, self.post_init) ############################################## @property def parser(self): return self._parser @property def args(self): return self._args @property def platform(self): return self._platform @property def settings(self): return self._settings @property def qml_application(self): return self._qml_application ############################################## def _print_critical_message(self, message): # print('\nCritical Error on {}'.format(datetime.datetime.now())) # print('-'*80) # print(message) self._logger.critical(message) ############################################## def _message_handler(self, msg_type, context, msg): if msg_type == QtCore.QtDebugMsg: method = self._logger.debug elif msg_type == QtCore.QtInfoMsg: method = self._logger.info elif msg_type == QtCore.QtWarningMsg: method = self._logger.warning elif msg_type in (QtCore.QtCriticalMsg, QtCore.QtFatalMsg): method = self._logger.critical # method = None # local_msg = msg.toLocal8Bit() # localMsg.constData() context_file = context.file if context_file is not None: file_path = Path(context_file).name else: file_path = '' message = '{1} {3} — {0}'.format(msg, file_path, context.line, context.function) if method is not None: method(message) else: self._print_critical_message(message) ############################################## def _on_critical_exception(self, exception): message = str(exception) + '\n' + traceback.format_exc() self._print_critical_message(message) self._qml_application.notify_error(exception) # sys.exit(1) ############################################## def _init_application(self): self._application.setOrganizationName( ApplicationMetadata.organisation_name) self._application.setOrganizationDomain( ApplicationMetadata.organisation_domain) self._application.setApplicationName(ApplicationMetadata.name) self._application.setApplicationDisplayName( ApplicationMetadata.display_name) self._application.setApplicationVersion(ApplicationMetadata.version) logo_path = ':/icons/logo/logo-256.png' self._application.setWindowIcon(QIcon(logo_path)) QIcon.setThemeName('material') self._settings = ApplicationSettings() ############################################## @classmethod def setup_gui_application(self): QGuiApplication.setAttribute(Qt.AA_EnableHighDpiScaling) # QQuickStyle.setStyle('Material') ############################################## def _parse_arguments(self): self._parser = argparse.ArgumentParser(description='CodeReview', ) self.parse_arguments() self._args = self._parser.parse_args() ############################################## def parse_arguments(self): # self.parser.add_argument( # '--version', # action='store_true', default=False, # help="show version and exit", # ) self.parser.add_argument( '--dont-translate', action='store_true', default=False, help="Don't translate application", ) self.parser.add_argument( '--user-script', action=PathAction, default=None, help='user script to execute', ) self.parser.add_argument( '--user-script-args', default='', help="user script args (don't forget to quote)", ) ############################################## def _load_translation(self): if self._args.dont_translate: return # Fixme: ConfigInstall # directory = ':/translations' directory = str(Path(__file__).parent.joinpath('rcc', 'translations')) locale = QtCore.QLocale() self._translator = QtCore.QTranslator() if self._translator.load(locale, 'code-review', '.', directory, '.qm'): self._application.installTranslator(self._translator) else: raise NameError('No translator for locale {}'.format( locale.name())) ############################################## def register_qml_types(self): qmlRegisterType(KeySequenceEditor, 'CodeReview', 1, 0, 'KeySequenceEditor') qmlRegisterUncreatableType(Shortcut, 'CodeReview', 1, 0, 'Shortcut', 'Cannot create Shortcut') qmlRegisterUncreatableType(ApplicationSettings, 'CodeReview', 1, 0, 'ApplicationSettings', 'Cannot create ApplicationSettings') qmlRegisterUncreatableType(self.QmlApplication_CLS, 'CodeReview', 1, 0, 'QmlApplication', 'Cannot create QmlApplication') ############################################## def _set_context_properties(self): context = self._engine.rootContext() context.setContextProperty('application', self._qml_application) context.setContextProperty('application_settings', self._settings) ############################################## def _load_qml_main(self): self._logger.info('Load QML...') qml_path = Path(__file__).parent.joinpath('qml') # qml_path = 'qrc:///qml' self._engine.addImportPath(str(qml_path)) main_qml_path = qml_path.joinpath('main.qml') self._qml_url = QUrl.fromLocalFile(str(main_qml_path)) # QUrl('qrc:/qml/main.qml') self._engine.objectCreated.connect(self._check_qml_is_loaded) self._engine.load(self._qml_url) self._logger.info('QML loaded') ############################################## def _check_qml_is_loaded(self, obj, url): # See https://bugreports.qt.io/browse/QTBUG-39469 if (obj is None and url == self._qml_url): sys.exit(-1) ############################################## def exec_(self): self._logger.info('Start event loop') rc = self._application.exec_() self._logger.info('Event loop done {}'.format(rc)) del self._engine # solve quit issue ? sys.exit(rc) ############################################## def aboutToQuit(self): self._logger.info('') ############################################## def post_init(self): # Fixme: ui refresh ??? self._logger.info('post Init...') if self._args.user_script is not None: self.execute_user_script(self._args.user_script) self._logger.info('Post Init Done') ############################################## def execute_user_script(self, script_path): """Execute an user script provided by file *script_path* in a context where is defined a variable *application* that is a reference to the application instance. """ script_path = Path(script_path).absolute() self._logger.info('Execute user script:\n {}'.format(script_path)) try: source = open(script_path).read() except FileNotFoundError: self._logger.info('File {} not found'.format(script_path)) sys.exit(1) try: bytecode = compile(source, script_path, 'exec') except SyntaxError as exception: self._on_critical_exception(exception) try: exec(bytecode, {'application': self}) except Exception as exception: self._on_critical_exception(exception) self._logger.info('User script done')