2013-12-14 22:15:16 +01:00
|
|
|
import sys
|
2014-01-17 20:03:21 +01:00
|
|
|
import logging
|
2014-01-20 15:58:49 +01:00
|
|
|
from signal import signal, SIGINT
|
|
|
|
from argparse import ArgumentParser
|
|
|
|
|
|
|
|
from PyQt5.QtWidgets import QApplication
|
2014-01-20 14:04:29 +01:00
|
|
|
from PyQt5.QtCore import QUrl, QTimer
|
2014-01-20 15:58:49 +01:00
|
|
|
|
|
|
|
import qutebrowser.commands.utils as cmdutils
|
2013-12-15 20:33:43 +01:00
|
|
|
from qutebrowser.widgets.mainwindow import MainWindow
|
2014-01-17 12:01:21 +01:00
|
|
|
from qutebrowser.commands.keys import KeyParser
|
2014-01-20 12:26:02 +01:00
|
|
|
from qutebrowser.utils.config import Config
|
|
|
|
from qutebrowser.utils.appdirs import AppDirs
|
2013-12-14 22:15:16 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
class QuteBrowser(QApplication):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Main object for QuteBrowser"""
|
|
|
|
dirs = None # AppDirs - config/cache directories
|
|
|
|
config = None # Config(Parser) object
|
|
|
|
mainwindow = None
|
|
|
|
commandparser = None
|
|
|
|
keyparser = None
|
|
|
|
args = None # ArgumentParser
|
|
|
|
timer = None # QTimer for python hacks
|
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
def __init__(self):
|
|
|
|
super().__init__(sys.argv)
|
2014-01-20 13:50:33 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
args = self.parseopts()
|
|
|
|
self.initlog()
|
2014-01-17 20:03:21 +01:00
|
|
|
|
2014-01-20 12:26:02 +01:00
|
|
|
self.dirs = AppDirs('qutebrowser')
|
2014-01-22 17:04:10 +01:00
|
|
|
if self.args.confdir is None:
|
|
|
|
confdir = self.dirs.user_data_dir
|
|
|
|
elif self.args.confdir == '':
|
|
|
|
confdir = None
|
|
|
|
else:
|
|
|
|
confdir = self.args.confdir
|
|
|
|
self.config = Config(confdir)
|
2014-01-20 12:26:02 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
self.mainwindow = MainWindow()
|
|
|
|
self.commandparser = cmdutils.CommandParser()
|
2014-01-20 17:20:17 +01:00
|
|
|
self.keyparser = KeyParser(self.mainwindow)
|
2014-01-17 20:03:21 +01:00
|
|
|
|
2014-01-20 15:58:49 +01:00
|
|
|
self.aboutToQuit.connect(self.config.save)
|
2014-01-19 19:41:34 +01:00
|
|
|
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
|
2014-01-19 18:20:57 +01:00
|
|
|
self.keyparser.set_cmd_text.connect(self.mainwindow.status.cmd.set_cmd)
|
2014-01-22 17:31:15 +01:00
|
|
|
self.mainwindow.status.cmd.got_cmd.connect(self.commandparser.run)
|
2014-01-20 15:58:49 +01:00
|
|
|
self.mainwindow.status.cmd.got_cmd.connect(
|
|
|
|
self.mainwindow.tabs.setFocus)
|
2014-01-19 18:20:57 +01:00
|
|
|
self.commandparser.error.connect(self.mainwindow.status.disp_error)
|
2014-01-20 17:20:17 +01:00
|
|
|
self.keyparser.commandparser.error.connect(
|
|
|
|
self.mainwindow.status.disp_error)
|
2014-01-20 07:01:39 +01:00
|
|
|
self.keyparser.keystring_updated.connect(
|
2014-01-20 15:58:49 +01:00
|
|
|
self.mainwindow.status.txt.set_keystring)
|
2014-01-17 20:03:21 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
self.init_cmds()
|
|
|
|
self.mainwindow.show()
|
2013-12-15 20:33:43 +01:00
|
|
|
|
2014-01-20 14:04:29 +01:00
|
|
|
self.python_hacks()
|
|
|
|
|
|
|
|
def python_hacks(self):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Gets around some PyQt-oddities by evil hacks"""
|
|
|
|
## Make python exceptions work
|
2014-01-20 14:04:29 +01:00
|
|
|
sys._excepthook = sys.excepthook
|
|
|
|
def exception_hook(exctype, value, traceback):
|
|
|
|
sys._excepthook(exctype, value, traceback)
|
|
|
|
# FIXME save open tabs here
|
2014-01-20 15:58:49 +01:00
|
|
|
self.exit(1)
|
2014-01-20 14:04:29 +01:00
|
|
|
sys.excepthook = exception_hook
|
|
|
|
|
2014-01-20 15:58:49 +01:00
|
|
|
## Quit on SIGINT
|
|
|
|
signal(SIGINT, lambda *args: self.exit(128 + SIGINT))
|
2014-01-20 14:04:29 +01:00
|
|
|
|
2014-01-20 15:58:49 +01:00
|
|
|
## hack to make Ctrl+C work by passing control to the Python
|
|
|
|
## interpreter once all 500ms (lambda to ignore args)
|
2014-01-20 14:04:29 +01:00
|
|
|
self.timer = QTimer()
|
|
|
|
self.timer.start(500)
|
|
|
|
self.timer.timeout.connect(lambda: None)
|
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
def parseopts(self):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Parse command line options"""
|
|
|
|
parser = ArgumentParser("usage: %(prog)s [options]")
|
2014-01-19 18:20:57 +01:00
|
|
|
parser.add_argument('-l', '--log', dest='loglevel',
|
2014-01-20 15:58:49 +01:00
|
|
|
help='Set loglevel', default='info')
|
2014-01-22 17:04:10 +01:00
|
|
|
parser.add_argument('-c', '--confdir', help='Set config directory '
|
|
|
|
'(empty for no config storage)')
|
2014-01-19 18:20:57 +01:00
|
|
|
self.args = parser.parse_args()
|
2013-12-15 20:33:43 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
def initlog(self):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Initialisation of the log"""
|
|
|
|
loglevel = self.args.loglevel
|
2014-01-19 18:20:57 +01:00
|
|
|
numeric_level = getattr(logging, loglevel.upper(), None)
|
|
|
|
if not isinstance(numeric_level, int):
|
2014-01-20 15:58:49 +01:00
|
|
|
raise ValueError('Invalid log level: {}'.format(loglevel))
|
2014-01-19 18:20:57 +01:00
|
|
|
logging.basicConfig(
|
|
|
|
level=numeric_level,
|
|
|
|
format='%(asctime)s [%(levelname)s] '
|
|
|
|
'[%(module)s:%(funcName)s:%(lineno)s] %(message)s',
|
|
|
|
datefmt='%Y-%m-%d %H:%M:%S')
|
2014-01-17 10:57:27 +01:00
|
|
|
|
2014-01-19 18:20:57 +01:00
|
|
|
def init_cmds(self):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Initialisation of the qutebrowser commands"""
|
2014-01-19 18:20:57 +01:00
|
|
|
cmdutils.register_all()
|
2014-01-20 15:58:49 +01:00
|
|
|
for cmd in cmdutils.cmd_dict.values():
|
2014-01-19 22:55:00 +01:00
|
|
|
cmd.signal.connect(self.cmd_handler)
|
2014-01-22 16:43:24 +01:00
|
|
|
try:
|
|
|
|
self.keyparser.from_config_sect(self.config['keybind'])
|
|
|
|
except KeyError:
|
|
|
|
pass
|
2014-01-19 22:55:00 +01:00
|
|
|
|
|
|
|
def cmd_handler(self, tpl):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Handler which gets called from all commands and delegates the
|
|
|
|
specific actions.
|
|
|
|
|
|
|
|
tpl -- A tuple in the form (count, argv) where argv is [cmd, arg, ...]
|
|
|
|
|
|
|
|
All handlers supporting a count should have a keyword argument count.
|
|
|
|
"""
|
2014-01-19 22:55:00 +01:00
|
|
|
(count, argv) = tpl
|
|
|
|
cmd = argv[0]
|
|
|
|
args = argv[1:]
|
|
|
|
|
2014-01-19 23:03:06 +01:00
|
|
|
handlers = {
|
2014-01-20 17:59:01 +01:00
|
|
|
'open': self.mainwindow.tabs.openurl,
|
|
|
|
'tabopen': self.mainwindow.tabs.tabopen,
|
|
|
|
'quit': self.quit,
|
|
|
|
'tabclose': self.mainwindow.tabs.cur_close,
|
|
|
|
'tabprev': self.mainwindow.tabs.switch_prev,
|
|
|
|
'tabnext': self.mainwindow.tabs.switch_next,
|
|
|
|
'reload': self.mainwindow.tabs.cur_reload,
|
|
|
|
'stop': self.mainwindow.tabs.cur_stop,
|
|
|
|
'back': self.mainwindow.tabs.cur_back,
|
|
|
|
'forward': self.mainwindow.tabs.cur_forward,
|
|
|
|
'print': self.mainwindow.tabs.cur_print,
|
|
|
|
'scroll': self.mainwindow.tabs.cur_scroll,
|
|
|
|
'scrollpercentx': self.mainwindow.tabs.cur_scroll_percent_x,
|
|
|
|
'scrollpercenty': self.mainwindow.tabs.cur_scroll_percent_y,
|
|
|
|
'undo': self.mainwindow.tabs.undo_close,
|
|
|
|
'pyeval': self.pyeval,
|
2014-01-19 23:03:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
handler = handlers[cmd]
|
|
|
|
|
2014-01-20 15:58:49 +01:00
|
|
|
if self.sender().count:
|
2014-01-19 23:03:06 +01:00
|
|
|
handler(*args, count=count)
|
|
|
|
else:
|
|
|
|
handler(*args)
|
2014-01-19 23:54:22 +01:00
|
|
|
|
|
|
|
def pyeval(self, s):
|
2014-01-20 15:58:49 +01:00
|
|
|
"""Evaluates a python string, handler for the pyeval command"""
|
2014-01-19 23:54:22 +01:00
|
|
|
try:
|
|
|
|
r = eval(s)
|
|
|
|
out = repr(r)
|
|
|
|
except Exception as e:
|
|
|
|
out = ': '.join([e.__class__.__name__, str(e)])
|
|
|
|
|
2014-01-20 15:58:49 +01:00
|
|
|
# FIXME we probably want some nicer interface to display these about:
|
|
|
|
# pages
|
2014-01-19 23:54:22 +01:00
|
|
|
tab = self.mainwindow.tabs.currentWidget()
|
2014-01-20 08:57:11 +01:00
|
|
|
tab.setUrl(QUrl('about:pyeval'))
|
2014-01-19 23:54:22 +01:00
|
|
|
tab.setContent(out.encode('UTF-8'), 'text/plain')
|