#!/usr/bin/env python3
# this project is licensed under the WTFPLv2, see COPYING.txt for details
import argparse
import glob
import logging
import os
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication, QMainWindow
from eye import pathutils, connector
from eye.qt import Slot
__all__ = ('App', 'qApp', 'main')
[docs]
def qApp():
return QApplication.instance()
[docs]
class App(QApplication):
"""Application"""
def __init__(self, argv):
super().__init__(argv)
self.setApplicationName('eye')
self.setApplicationDisplayName('eye')
self.logger = logging.getLogger()
self.args = None
self.last_window = None
self.focusChanged.connect(self._app_focus_changed)
self.setWindowIcon(QIcon(pathutils.data_path('eye.png')))
[docs]
def init_ui(self):
from eye.widgets import window
win = window.Window()
win.create_default_menu_bar()
win.quit_requested.connect(self.quit)
return win
[docs]
def startup_scripts(self):
"""Get list of startup script files
These are the script present at the moment, not the scripts that were run when the app started.
"""
files = glob.glob(os.path.join(pathutils.get_config_path('startup'), '*.py'))
files.sort()
return files
[docs]
def script_dict(self):
"""Build a env suitable for running conf scripts.
The built dict will contain `'qApp'` key pointing to this App instance.
"""
return {'qApp': QApplication.instance()}
[docs]
def run_start_scripts(self):
for f in self.startup_scripts():
self.run_script(f)
[docs]
def run_script(self, path):
"""Run a config script in this app
The script will be run with the variables returned by :any:`script_dict`.
Exceptions thrown by the script are catched and logged.
"""
self.logger.debug('execing script %s', path)
try:
execfile(path, self.script_dict())
except Exception:
self.logger.error('cannot execute startup script %r', path, exc_info=True)
[docs]
def run(self):
"""Run app until exit
Create and show interface, run config script, handle command-line args and run.
Does not return until app is quit.
"""
self.parse_arguments()
self.init_logging()
if self.args.remote and self.process_remote():
return 0
welcome = False
if not self.args.no_config:
if not self.startup_scripts():
from eye.helpers.welcome import ask_to_copy, open_welcome_text
welcome = ask_to_copy()
self.run_start_scripts()
win = self.init_ui()
win.show()
self.open_command_line_files()
if welcome:
open_welcome_text()
return self.exec_()
[docs]
def parse_arguments(self):
parser = argparse.ArgumentParser()
parser.add_argument('files', metavar='FILE', nargs='*')
parser.add_argument('--debug', action='store_true', default=False)
parser.add_argument('--debug-only', action='append', default=[])
parser.add_argument('--no-config', action='store_true', default=False)
parser.add_argument('--remote', action='store_true', default=False)
argv = self.arguments()[1:]
self.args = parser.parse_args(argv)
[docs]
def init_logging(self):
if self.args.debug:
self.logger.handlers[0].setLevel(logging.DEBUG)
for logger_name in self.args.debug_only:
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(levelname)s:%(name)s:%(message)s'))
handler.setLevel(logging.DEBUG)
logger = logging.getLogger(logger_name)
logger.addHandler(handler)
[docs]
def process_remote(self):
from eye.helpers import remote_control
try:
remote_control.send_request('ping')
except ValueError:
remote_control.create_server()
return False
try:
path, row = pathutils.vim_filename_arg(self.args.files)
except TypeError:
pass
else:
path = os.path.abspath(path)
remote_control.send_request('open', "%s:%s" % (path, row))
return True
for path in self.args.files:
path = os.path.abspath(path)
remote_control.send_request('open', path)
return True
[docs]
def open_command_line_files(self):
if not self.args.files:
return
win = connector.category_objects('window')[0]
from eye.helpers.intent import send_intent
try:
path, row = pathutils.vim_filename_arg(self.args.files)
except TypeError:
pass
else:
send_intent(win, 'open_editor', path=path, loc=(row,), reason='commandline')
# only 1 filename in this case
return
for name in self.args.files:
path, row, col = pathutils.parse_filename(name)
path = os.path.abspath(path)
loc = None
if row and col:
loc = (row, col)
elif row:
loc = (row,)
send_intent(win, 'open_editor', path=path, loc=loc, reason='commandline')
@Slot('QWidget*', 'QWidget*')
def _app_focus_changed(self, old, new):
while new and not new.isWindow():
new = new.parentWidget()
if not new or not isinstance(new, QMainWindow):
# exclude dialogs
return
self.last_window = new
def setup_logging():
logging.basicConfig()
root = logging.getLogger()
root.setLevel(logging.DEBUG)
root.handlers[0].setLevel(logging.WARNING)
def execfile(path, globals):
"""Exec Python `file` with `globals` as in Python 2"""
with open(path) as fd:
src = fd.read()
code = compile(src, path, 'exec')
exec(code, globals) # pylint: disable=exec-used
[docs]
def main():
"""Run eye app"""
# if the default excepthook is used, PyQt 5.5 *aborts* the app when an unhandled exception occurs
# see http://pyqt.sourceforge.net/Docs/PyQt5/incompatibilities.html
# as this behaviour is questionable, we restore the old one
if sys.excepthook is sys.__excepthook__:
sys.excepthook = lambda *args: sys.__excepthook__(*args)
setup_logging()
app = App(sys.argv)
return app.run()
if __name__ == '__main__':
sys.exit(main())