Refactor ALL the things
- Remove super() where unneeded - Add docstrings where applicable - Remove setObjectName calls - Lots and lots of smaller changes
This commit is contained in:
parent
dfe7d6c7ef
commit
1095e24f98
@ -1,16 +1,27 @@
|
|||||||
import sys
|
import sys
|
||||||
import argparse
|
|
||||||
import logging
|
import logging
|
||||||
import signal
|
from signal import signal, SIGINT
|
||||||
from PyQt5.QtWidgets import QWidget, QApplication
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
from PyQt5.QtCore import QUrl, QTimer
|
from PyQt5.QtCore import QUrl, QTimer
|
||||||
|
|
||||||
|
import qutebrowser.commands.utils as cmdutils
|
||||||
from qutebrowser.widgets.mainwindow import MainWindow
|
from qutebrowser.widgets.mainwindow import MainWindow
|
||||||
from qutebrowser.commands.keys import KeyParser
|
from qutebrowser.commands.keys import KeyParser
|
||||||
from qutebrowser.utils.config import Config
|
from qutebrowser.utils.config import Config
|
||||||
import qutebrowser.commands.utils as cmdutils
|
|
||||||
from qutebrowser.utils.appdirs import AppDirs
|
from qutebrowser.utils.appdirs import AppDirs
|
||||||
|
|
||||||
class QuteBrowser(QApplication):
|
class QuteBrowser(QApplication):
|
||||||
|
"""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
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(sys.argv)
|
super().__init__(sys.argv)
|
||||||
|
|
||||||
@ -24,11 +35,12 @@ class QuteBrowser(QApplication):
|
|||||||
self.commandparser = cmdutils.CommandParser()
|
self.commandparser = cmdutils.CommandParser()
|
||||||
self.keyparser = KeyParser(self.mainwindow)
|
self.keyparser = KeyParser(self.mainwindow)
|
||||||
|
|
||||||
super().aboutToQuit.connect(self.config.save)
|
self.aboutToQuit.connect(self.config.save)
|
||||||
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
|
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
|
||||||
self.keyparser.set_cmd_text.connect(self.mainwindow.status.cmd.set_cmd)
|
self.keyparser.set_cmd_text.connect(self.mainwindow.status.cmd.set_cmd)
|
||||||
self.mainwindow.status.cmd.got_cmd.connect(self.commandparser.parse)
|
self.mainwindow.status.cmd.got_cmd.connect(self.commandparser.parse)
|
||||||
self.mainwindow.status.cmd.got_cmd.connect(self.mainwindow.tabs.setFocus)
|
self.mainwindow.status.cmd.got_cmd.connect(
|
||||||
|
self.mainwindow.tabs.setFocus)
|
||||||
self.commandparser.error.connect(self.mainwindow.status.disp_error)
|
self.commandparser.error.connect(self.mainwindow.status.disp_error)
|
||||||
self.keyparser.keystring_updated.connect(
|
self.keyparser.keystring_updated.connect(
|
||||||
self.mainwindow.status.txt.set_keystring)
|
self.mainwindow.status.txt.set_keystring)
|
||||||
@ -39,41 +51,37 @@ class QuteBrowser(QApplication):
|
|||||||
self.python_hacks()
|
self.python_hacks()
|
||||||
|
|
||||||
def python_hacks(self):
|
def python_hacks(self):
|
||||||
qapp = super(QApplication, self)
|
"""Gets around some PyQt-oddities by evil hacks"""
|
||||||
|
## Make python exceptions work
|
||||||
### Make python exceptions work
|
|
||||||
sys._excepthook = sys.excepthook
|
sys._excepthook = sys.excepthook
|
||||||
def exception_hook(exctype, value, traceback):
|
def exception_hook(exctype, value, traceback):
|
||||||
sys._excepthook(exctype, value, traceback)
|
sys._excepthook(exctype, value, traceback)
|
||||||
# FIXME save open tabs here
|
# FIXME save open tabs here
|
||||||
qapp.exit(1)
|
self.exit(1)
|
||||||
sys.excepthook = exception_hook
|
sys.excepthook = exception_hook
|
||||||
|
|
||||||
### Quit on SIGINT
|
## Quit on SIGINT
|
||||||
signal.signal(signal.SIGINT, lambda *args:
|
signal(SIGINT, lambda *args: self.exit(128 + SIGINT))
|
||||||
qapp.exit(128 + signal.SIGINT))
|
|
||||||
|
|
||||||
### hack to make Ctrl+C work by passing control to the Python
|
## hack to make Ctrl+C work by passing control to the Python
|
||||||
### interpreter once all 500ms (lambda to ignore args)
|
## interpreter once all 500ms (lambda to ignore args)
|
||||||
self.timer = QTimer()
|
self.timer = QTimer()
|
||||||
self.timer.start(500)
|
self.timer.start(500)
|
||||||
self.timer.timeout.connect(lambda: None)
|
self.timer.timeout.connect(lambda: None)
|
||||||
|
|
||||||
def parseopts(self):
|
def parseopts(self):
|
||||||
parser = argparse.ArgumentParser("usage: %(prog)s [options]")
|
"""Parse command line options"""
|
||||||
|
parser = ArgumentParser("usage: %(prog)s [options]")
|
||||||
parser.add_argument('-l', '--log', dest='loglevel',
|
parser.add_argument('-l', '--log', dest='loglevel',
|
||||||
help='Set loglevel', default=0)
|
help='Set loglevel', default='info')
|
||||||
self.args = parser.parse_args()
|
self.args = parser.parse_args()
|
||||||
|
|
||||||
def initlog(self):
|
def initlog(self):
|
||||||
""" Initialisation of the log """
|
"""Initialisation of the log"""
|
||||||
if self.args.loglevel:
|
|
||||||
loglevel = self.args.loglevel
|
loglevel = self.args.loglevel
|
||||||
else:
|
|
||||||
loglevel = 'info'
|
|
||||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||||
if not isinstance(numeric_level, int):
|
if not isinstance(numeric_level, int):
|
||||||
raise ValueError('Invalid log level: %s' % loglevel)
|
raise ValueError('Invalid log level: {}'.format(loglevel))
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=numeric_level,
|
level=numeric_level,
|
||||||
format='%(asctime)s [%(levelname)s] '
|
format='%(asctime)s [%(levelname)s] '
|
||||||
@ -81,13 +89,20 @@ class QuteBrowser(QApplication):
|
|||||||
datefmt='%Y-%m-%d %H:%M:%S')
|
datefmt='%Y-%m-%d %H:%M:%S')
|
||||||
|
|
||||||
def init_cmds(self):
|
def init_cmds(self):
|
||||||
|
"""Initialisation of the qutebrowser commands"""
|
||||||
cmdutils.register_all()
|
cmdutils.register_all()
|
||||||
cmds = cmdutils.cmd_dict
|
for cmd in cmdutils.cmd_dict.values():
|
||||||
for cmd in cmds.values():
|
|
||||||
cmd.signal.connect(self.cmd_handler)
|
cmd.signal.connect(self.cmd_handler)
|
||||||
self.keyparser.from_config_sect(self.config['keybind'])
|
self.keyparser.from_config_sect(self.config['keybind'])
|
||||||
|
|
||||||
def cmd_handler(self, tpl):
|
def cmd_handler(self, tpl):
|
||||||
|
"""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.
|
||||||
|
"""
|
||||||
(count, argv) = tpl
|
(count, argv) = tpl
|
||||||
cmd = argv[0]
|
cmd = argv[0]
|
||||||
args = argv[1:]
|
args = argv[1:]
|
||||||
@ -95,41 +110,42 @@ class QuteBrowser(QApplication):
|
|||||||
handlers = {
|
handlers = {
|
||||||
'open': self.mainwindow.tabs.openurl,
|
'open': self.mainwindow.tabs.openurl,
|
||||||
'tabopen': self.mainwindow.tabs.tabopen,
|
'tabopen': self.mainwindow.tabs.tabopen,
|
||||||
'quit': super().quit,
|
'quit': self.quit,
|
||||||
'tabclose': self.mainwindow.tabs.close_act,
|
'tabclose': self.mainwindow.tabs.cur_close,
|
||||||
'tabprev': self.mainwindow.tabs.switch_prev,
|
'tabprev': self.mainwindow.tabs.switch_prev,
|
||||||
'tabnext': self.mainwindow.tabs.switch_next,
|
'tabnext': self.mainwindow.tabs.switch_next,
|
||||||
'reload': self.mainwindow.tabs.reload_act,
|
'reload': self.mainwindow.tabs.cur_reload,
|
||||||
'stop': self.mainwindow.tabs.stop_act,
|
'stop': self.mainwindow.tabs.cur_stop,
|
||||||
'back': self.mainwindow.tabs.back_act,
|
'back': self.mainwindow.tabs.cur_back,
|
||||||
'forward': self.mainwindow.tabs.forward_act,
|
'forward': self.mainwindow.tabs.cur_forward,
|
||||||
'print': self.mainwindow.tabs.print_act,
|
'print': self.mainwindow.tabs.cur_print,
|
||||||
'scrolldown': self.mainwindow.tabs.scroll_down_act,
|
'scrolldown': self.mainwindow.tabs.cur_scroll_down,
|
||||||
'scrollup': self.mainwindow.tabs.scroll_up_act,
|
'scrollup': self.mainwindow.tabs.cur_scroll_up,
|
||||||
'scrollleft': self.mainwindow.tabs.scroll_left_act,
|
'scrollleft': self.mainwindow.tabs.cur_scroll_left,
|
||||||
'scrollright': self.mainwindow.tabs.scroll_right_act,
|
'scrollright': self.mainwindow.tabs.cur_scroll_right,
|
||||||
'scrollstart': self.mainwindow.tabs.scroll_start_act,
|
'scrollstart': self.mainwindow.tabs.cur_scroll_start,
|
||||||
'scrollend': self.mainwindow.tabs.scroll_end_act,
|
'scrollend': self.mainwindow.tabs.cur_scroll_end,
|
||||||
'undo': self.mainwindow.tabs.undo_close,
|
'undo': self.mainwindow.tabs.undo_close,
|
||||||
'pyeval': self.pyeval
|
'pyeval': self.pyeval,
|
||||||
}
|
}
|
||||||
|
|
||||||
handler = handlers[cmd]
|
handler = handlers[cmd]
|
||||||
sender = self.sender()
|
|
||||||
|
|
||||||
if sender.count:
|
if self.sender().count:
|
||||||
handler(*args, count=count)
|
handler(*args, count=count)
|
||||||
else:
|
else:
|
||||||
handler(*args)
|
handler(*args)
|
||||||
|
|
||||||
def pyeval(self, s):
|
def pyeval(self, s):
|
||||||
|
"""Evaluates a python string, handler for the pyeval command"""
|
||||||
try:
|
try:
|
||||||
r = eval(s)
|
r = eval(s)
|
||||||
out = repr(r)
|
out = repr(r)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
out = ': '.join([e.__class__.__name__, str(e)])
|
out = ': '.join([e.__class__.__name__, str(e)])
|
||||||
|
|
||||||
# FIXME we probably want some nicer interface to display these about: pages
|
# FIXME we probably want some nicer interface to display these about:
|
||||||
|
# pages
|
||||||
tab = self.mainwindow.tabs.currentWidget()
|
tab = self.mainwindow.tabs.currentWidget()
|
||||||
tab.setUrl(QUrl('about:pyeval'))
|
tab.setUrl(QUrl('about:pyeval'))
|
||||||
tab.setContent(out.encode('UTF-8'), 'text/plain')
|
tab.setContent(out.encode('UTF-8'), 'text/plain')
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
from PyQt5.QtCore import pyqtSignal
|
|
||||||
from qutebrowser.commands.utils import Command
|
from qutebrowser.commands.utils import Command
|
||||||
|
|
||||||
|
"""All command classes. These are automatically propagated from commands.utils
|
||||||
|
via inspect.
|
||||||
|
|
||||||
|
A command class can set the following properties:
|
||||||
|
|
||||||
|
nargs -- Number of arguments. Either a number, '?' (0 or 1), '+' (1 or
|
||||||
|
more), or '*' (any). Default: 0
|
||||||
|
|
||||||
|
split_args -- If arguments should be split or not. Default: True
|
||||||
|
|
||||||
|
count -- If the command supports a count. Default: False
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Open(Command):
|
class Open(Command):
|
||||||
nargs = 1
|
nargs = 1
|
||||||
split_args = False
|
split_args = False
|
||||||
|
@ -1,26 +1,43 @@
|
|||||||
from PyQt5.QtCore import QObject, Qt, pyqtSignal
|
|
||||||
from PyQt5.QtWidgets import QShortcut
|
|
||||||
from PyQt5.QtGui import QKeySequence
|
|
||||||
import qutebrowser.commands.utils as cmdutils
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QObject, pyqtSignal
|
||||||
|
|
||||||
|
import qutebrowser.commands.utils as cmdutils
|
||||||
|
|
||||||
class KeyParser(QObject):
|
class KeyParser(QObject):
|
||||||
keystring = ''
|
"""Parser for vim-like key sequences"""
|
||||||
|
keystring = '' # The currently entered key sequence
|
||||||
|
# Signal emitted when the statusbar should set a partial command
|
||||||
set_cmd_text = pyqtSignal(str)
|
set_cmd_text = pyqtSignal(str)
|
||||||
|
# Signal emitted when the keystring is updated
|
||||||
keystring_updated = pyqtSignal(str)
|
keystring_updated = pyqtSignal(str)
|
||||||
|
# Keybindings
|
||||||
key_to_cmd = {}
|
key_to_cmd = {}
|
||||||
|
|
||||||
|
MATCH_PARTIAL = 0
|
||||||
|
MATCH_DEFINITIVE = 1
|
||||||
|
MATCH_NONE = 2
|
||||||
|
|
||||||
def from_config_sect(self, sect):
|
def from_config_sect(self, sect):
|
||||||
|
"""Loads keybindings from a ConfigParser section, in the config format
|
||||||
|
key = command, e.g.
|
||||||
|
gg = scrollstart
|
||||||
|
"""
|
||||||
for (key, cmd) in sect.items():
|
for (key, cmd) in sect.items():
|
||||||
logging.debug('registered: {} -> {}'.format(key, cmd))
|
logging.debug('registered key: {} -> {}'.format(key, cmd))
|
||||||
self.key_to_cmd[key] = cmdutils.cmd_dict[cmd]
|
self.key_to_cmd[key] = cmdutils.cmd_dict[cmd]
|
||||||
|
|
||||||
def handle(self, e):
|
def handle(self, e):
|
||||||
|
"""Wrapper for _handle to emit keystring_updated after _handle"""
|
||||||
self._handle(e)
|
self._handle(e)
|
||||||
self.keystring_updated.emit(self.keystring)
|
self.keystring_updated.emit(self.keystring)
|
||||||
|
|
||||||
def _handle(self, e):
|
def _handle(self, e):
|
||||||
|
"""Handle a new keypress.
|
||||||
|
|
||||||
|
e -- the KeyPressEvent from Qt
|
||||||
|
"""
|
||||||
logging.debug('Got key: {} / text: "{}"'.format(e.key(), e.text()))
|
logging.debug('Got key: {} / text: "{}"'.format(e.key(), e.text()))
|
||||||
txt = e.text().strip()
|
txt = e.text().strip()
|
||||||
if not txt:
|
if not txt:
|
||||||
@ -44,14 +61,16 @@ class KeyParser(QObject):
|
|||||||
# If a keychain is ambigious, we probably should set up a QTimer with a
|
# If a keychain is ambigious, we probably should set up a QTimer with a
|
||||||
# configurable timeout, which triggers cmd.run() when expiring. Then
|
# configurable timeout, which triggers cmd.run() when expiring. Then
|
||||||
# when we enter _handle() again in time we stop the timer.
|
# when we enter _handle() again in time we stop the timer.
|
||||||
try:
|
|
||||||
cmd = self.key_to_cmd[cmdstr]
|
(match, cmd) = self._match_key(cmdstr)
|
||||||
except KeyError:
|
|
||||||
if self._partial_match(cmdstr, txt):
|
if match == self.MATCH_DEFINITIVE:
|
||||||
logging.debug('No match for "{}" (added {})'.format(
|
pass
|
||||||
self.keystring, txt))
|
elif match == self.MATCH_PARTIAL:
|
||||||
|
logging.debug('No match for "{}" (added {})'.format(self.keystring,
|
||||||
|
txt))
|
||||||
return
|
return
|
||||||
else:
|
elif match == self.MATCH_NONE:
|
||||||
logging.debug('Giving up with "{}", no matches'.format(
|
logging.debug('Giving up with "{}", no matches'.format(
|
||||||
self.keystring))
|
self.keystring))
|
||||||
self.keystring = ''
|
self.keystring = ''
|
||||||
@ -69,13 +88,19 @@ class KeyParser(QObject):
|
|||||||
else:
|
else:
|
||||||
cmd.run()
|
cmd.run()
|
||||||
|
|
||||||
def _partial_match(self, cmdstr, txt):
|
def _match_key(self, cmdstr):
|
||||||
pos = len(cmdstr)
|
"""Tries to match a given cmdstr with any defined command"""
|
||||||
|
try:
|
||||||
|
cmd = self.key_to_cmd[cmdstr]
|
||||||
|
return (self.MATCH_DEFINITIVE, cmd)
|
||||||
|
except KeyError:
|
||||||
|
# No definitive match, check if there's a chance of a partial match
|
||||||
for cmd in self.key_to_cmd:
|
for cmd in self.key_to_cmd:
|
||||||
try:
|
try:
|
||||||
if cmdstr[-1] == cmd[pos-1]:
|
if cmdstr[-1] == cmd[len(cmdstr) - 1]:
|
||||||
return True
|
return (self.MATCH_PARTIAL, None)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
# current cmd is shorter than our cmdstr, so it won't match
|
||||||
continue
|
continue
|
||||||
else:
|
# no definitive and no partial matches if we arrived here
|
||||||
return False
|
return (self.MATCH_NONE, None)
|
||||||
|
@ -1,31 +1,37 @@
|
|||||||
|
"""Various command utils and the Command base class"""
|
||||||
|
|
||||||
import inspect
|
import inspect
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
import shlex
|
import shlex
|
||||||
|
|
||||||
from PyQt5.QtCore import QObject, pyqtSignal
|
from PyQt5.QtCore import QObject, pyqtSignal
|
||||||
|
|
||||||
cmd_dict = {}
|
cmd_dict = {}
|
||||||
|
|
||||||
def register_all():
|
class ArgumentCountError(TypeError):
|
||||||
import qutebrowser.commands.commands
|
pass
|
||||||
def is_cmd(obj):
|
|
||||||
return (inspect.isclass(obj) and
|
|
||||||
obj.__module__ == 'qutebrowser.commands.commands')
|
|
||||||
|
|
||||||
for (name, cls) in inspect.getmembers(qutebrowser.commands.commands,
|
def register_all():
|
||||||
is_cmd):
|
"""Register and initialize all commands."""
|
||||||
if cls.bind:
|
# We do this here to avoid a circular import, since commands.commands
|
||||||
|
# imports Command from this module.
|
||||||
|
import qutebrowser.commands.commands
|
||||||
|
for (name, cls) in inspect.getmembers(
|
||||||
|
qutebrowser.commands.commands, (lambda o: inspect.isclass(o) and
|
||||||
|
o.__module__ == 'qutebrowser.commands.commands')):
|
||||||
obj = cls()
|
obj = cls()
|
||||||
cmd_dict[obj.name] = obj
|
cmd_dict[obj.name] = obj
|
||||||
|
|
||||||
class CommandParser(QObject):
|
class CommandParser(QObject):
|
||||||
error = pyqtSignal(str)
|
"""Parser for qutebrowser commandline commands"""
|
||||||
|
error = pyqtSignal(str) # Emitted if there's an error
|
||||||
|
|
||||||
def parse(self, text):
|
def parse(self, text):
|
||||||
|
"""Parses a command and runs its handler"""
|
||||||
parts = text.strip().split(maxsplit=1)
|
parts = text.strip().split(maxsplit=1)
|
||||||
|
|
||||||
# FIXME maybe we should handle unambigious shorthands for commands here?
|
# FIXME maybe we should handle unambigious shorthands for commands
|
||||||
# Or at least we should add :q for :quit.
|
# here? Or at least we should add :q for :quit.
|
||||||
cmd = parts[0]
|
cmd = parts[0]
|
||||||
try:
|
try:
|
||||||
obj = cmd_dict[cmd]
|
obj = cmd_dict[cmd]
|
||||||
@ -42,17 +48,19 @@ class CommandParser(QObject):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
obj.check(args)
|
obj.check(args)
|
||||||
except TypeError:
|
except ArgumentCountError:
|
||||||
self.error.emit("{}: invalid argument count".format(cmd))
|
self.error.emit("{}: invalid argument count".format(cmd))
|
||||||
return
|
return
|
||||||
obj.run(args)
|
obj.run(args)
|
||||||
|
|
||||||
class Command(QObject):
|
class Command(QObject):
|
||||||
|
"""Base skeleton for a command. See the module help for
|
||||||
|
qutebrowser.commands.commands for details.
|
||||||
|
"""
|
||||||
nargs = 0
|
nargs = 0
|
||||||
name = None
|
name = None
|
||||||
signal = None
|
signal = None
|
||||||
count = False
|
count = False
|
||||||
bind = True
|
|
||||||
split_args = True
|
split_args = True
|
||||||
signal = pyqtSignal(tuple)
|
signal = pyqtSignal(tuple)
|
||||||
|
|
||||||
@ -62,16 +70,28 @@ class Command(QObject):
|
|||||||
self.name = self.__class__.__name__.lower()
|
self.name = self.__class__.__name__.lower()
|
||||||
|
|
||||||
def check(self, args):
|
def check(self, args):
|
||||||
|
"""Check if the argument count is valid. Raise ArgumentCountError if
|
||||||
|
not.
|
||||||
|
"""
|
||||||
if ((isinstance(self.nargs, int) and len(args) != self.nargs) or
|
if ((isinstance(self.nargs, int) and len(args) != self.nargs) or
|
||||||
(self.nargs == '?' and len(args) > 1) or
|
(self.nargs == '?' and len(args) > 1) or
|
||||||
(self.nargs == '+' and len(args) < 1)):
|
(self.nargs == '+' and len(args) < 1)):
|
||||||
raise TypeError("Invalid argument count!")
|
# for nargs == '*', anything is okay
|
||||||
|
raise ArgumentCountError
|
||||||
|
|
||||||
def run(self, args=None, count=None):
|
def run(self, args=None, count=None):
|
||||||
countstr = ' * {}'.format(count) if count is not None else ''
|
"""Runs the command.
|
||||||
argstr = ", ".join(args) if args else ''
|
|
||||||
logging.debug("Cmd called: {}({}){}".format(self.name, argstr,
|
args -- Arguments to the command.
|
||||||
countstr))
|
count -- Command repetition count.
|
||||||
|
"""
|
||||||
|
dbgout = ["command called:", self.name]
|
||||||
|
if args:
|
||||||
|
dbgout += args
|
||||||
|
if count is not None:
|
||||||
|
dbgout.append("* {}".format(count))
|
||||||
|
logging.debug(' '.join(dbgout))
|
||||||
|
|
||||||
argv = [self.name]
|
argv = [self.name]
|
||||||
if args is not None:
|
if args is not None:
|
||||||
argv += args
|
argv += args
|
||||||
|
@ -1,106 +1,128 @@
|
|||||||
from PyQt5.QtCore import QObject, pyqtSlot, QUrl, pyqtSignal, Qt, QPoint
|
|
||||||
from PyQt5.QtPrintSupport import QPrintPreviewDialog
|
|
||||||
from PyQt5.QtWebKitWidgets import QWebView
|
|
||||||
from qutebrowser.widgets.tabbar import TabWidget
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from PyQt5.QtCore import QUrl, pyqtSignal, Qt, QPoint
|
||||||
|
from PyQt5.QtPrintSupport import QPrintPreviewDialog
|
||||||
|
from PyQt5.QtWebKitWidgets import QWebView
|
||||||
|
|
||||||
|
from qutebrowser.widgets.tabbar import TabWidget
|
||||||
|
|
||||||
class TabbedBrowser(TabWidget):
|
class TabbedBrowser(TabWidget):
|
||||||
cur_progress = pyqtSignal(int)
|
"""A TabWidget with QWebViews inside"""
|
||||||
cur_load_finished = pyqtSignal(bool)
|
|
||||||
|
cur_progress = pyqtSignal(int) # Progress of the current tab changed
|
||||||
|
cur_load_finished = pyqtSignal(bool) # Current tab finished loading
|
||||||
keypress = pyqtSignal('QKeyEvent')
|
keypress = pyqtSignal('QKeyEvent')
|
||||||
url_stack = []
|
_url_stack = [] # Stack of URLs of closed tabs
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.currentChanged.connect(self.index_changed)
|
self.currentChanged.connect(self._currentChanged_handler)
|
||||||
self.tabopen("http://ddg.gg/")
|
self.tabopen("http://ddg.gg/")
|
||||||
|
|
||||||
def tabopen(self, url):
|
def tabopen(self, url):
|
||||||
|
"""Opens a new tab with a given url"""
|
||||||
tab = BrowserTab(self)
|
tab = BrowserTab(self)
|
||||||
tab.openurl(url)
|
tab.openurl(url)
|
||||||
self.addTab(tab, url)
|
self.addTab(tab, url)
|
||||||
self.setCurrentWidget(tab)
|
self.setCurrentWidget(tab)
|
||||||
self.progress_changed(tab.progress)
|
self.cur_progress.emit(tab.progress)
|
||||||
tab.loadProgress.connect(self.progress_changed)
|
tab.loadProgress.connect(
|
||||||
tab.loadFinished.connect(self.load_finished)
|
lambda *args: self._filter_signals(self.cur_progress, *args))
|
||||||
|
tab.loadFinished.connect(
|
||||||
|
lambda *args: self._filter_signals(self.cur_load_finished, *args))
|
||||||
# FIXME should we really bind this to loadStarted? Sometimes the URL
|
# FIXME should we really bind this to loadStarted? Sometimes the URL
|
||||||
# isn't set correctly at this point, e.g. when doing
|
# isn't set correctly at this point, e.g. when doing
|
||||||
# setContent(..., baseUrl=QUrl('foo'))
|
# setContent(..., baseUrl=QUrl('foo'))
|
||||||
tab.loadStarted.connect(self.init_title)
|
tab.loadStarted.connect(self._loadStarted_handler)
|
||||||
tab.titleChanged.connect(self.update_title)
|
tab.titleChanged.connect(self._titleChanged_handler)
|
||||||
|
|
||||||
def openurl(self, url):
|
def openurl(self, url):
|
||||||
|
"""Opens an url in the current tab"""
|
||||||
tab = self.currentWidget()
|
tab = self.currentWidget()
|
||||||
tab.openurl(url)
|
tab.openurl(url)
|
||||||
|
|
||||||
def undo_close(self):
|
def undo_close(self):
|
||||||
if self.url_stack:
|
"""Undos closing a tab"""
|
||||||
self.tabopen(self.url_stack.pop())
|
if self._url_stack:
|
||||||
|
self.tabopen(self._url_stack.pop())
|
||||||
|
|
||||||
def close_act(self):
|
def cur_close(self):
|
||||||
|
"""Closes the current tab"""
|
||||||
if self.count() > 1:
|
if self.count() > 1:
|
||||||
idx = self.currentIndex()
|
idx = self.currentIndex()
|
||||||
tab = self.currentWidget()
|
tab = self.currentWidget()
|
||||||
# FIXME maybe we should add the QUrl object here and deal with QUrls everywhere
|
# FIXME maybe we should add the QUrl object here and deal with QUrls everywhere
|
||||||
# FIXME maybe we actually should store the webview objects here
|
# FIXME maybe we actually should store the webview objects here
|
||||||
self.url_stack.append(tab.url().url())
|
self._url_stack.append(tab.url().url())
|
||||||
self.removeTab(idx)
|
self.removeTab(idx)
|
||||||
else:
|
else:
|
||||||
# FIXME
|
# FIXME
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def reload_act(self):
|
def cur_reload(self):
|
||||||
|
"""Reloads the current tab"""
|
||||||
self.currentWidget().reload()
|
self.currentWidget().reload()
|
||||||
|
|
||||||
def stop_act(self):
|
def cur_stop(self):
|
||||||
|
"""Stops loading in the current tab"""
|
||||||
self.currentWidget().stop()
|
self.currentWidget().stop()
|
||||||
|
|
||||||
def print_act(self):
|
def cur_print(self):
|
||||||
|
"""Prints the current tab"""
|
||||||
# FIXME that does not what I expect
|
# FIXME that does not what I expect
|
||||||
preview = QPrintPreviewDialog()
|
preview = QPrintPreviewDialog()
|
||||||
preview.paintRequested.connect(self.currentWidget().print)
|
preview.paintRequested.connect(self.currentWidget().print)
|
||||||
preview.exec_()
|
preview.exec_()
|
||||||
|
|
||||||
def back_act(self):
|
def cur_back(self):
|
||||||
|
"""Goes back in the history of the current tab"""
|
||||||
# FIXME display warning if beginning of history
|
# FIXME display warning if beginning of history
|
||||||
self.currentWidget().back()
|
self.currentWidget().back()
|
||||||
|
|
||||||
def forward_act(self):
|
def cur_forward(self):
|
||||||
|
"""Goes forward in the history of the current tab"""
|
||||||
# FIXME display warning if end of history
|
# FIXME display warning if end of history
|
||||||
self.currentWidget().forward()
|
self.currentWidget().forward()
|
||||||
|
|
||||||
def scroll_down_act(self, count=None):
|
def cur_scroll_down(self, count=None):
|
||||||
|
"""Scrolls the current tab down"""
|
||||||
if count is None:
|
if count is None:
|
||||||
count = 50
|
count = 50
|
||||||
self.currentWidget().page().mainFrame().scroll(0, count)
|
self.currentWidget().page().mainFrame().scroll(0, count)
|
||||||
|
|
||||||
def scroll_up_act(self, count=None):
|
def cur_scroll_up(self, count=None):
|
||||||
|
"""Scrolls the current tab up"""
|
||||||
if count is None:
|
if count is None:
|
||||||
count = 50
|
count = 50
|
||||||
self.currentWidget().page().mainFrame().scroll(0, -count)
|
self.currentWidget().page().mainFrame().scroll(0, -count)
|
||||||
|
|
||||||
def scroll_left_act(self, count=None):
|
def cur_scroll_left(self, count=None):
|
||||||
|
"""Scrolls the current tab left"""
|
||||||
if count is None:
|
if count is None:
|
||||||
count = 50
|
count = 50
|
||||||
self.currentWidget().page().mainFrame().scroll(-count, 0)
|
self.currentWidget().page().mainFrame().scroll(-count, 0)
|
||||||
|
|
||||||
def scroll_right_act(self, count=None):
|
def cur_scroll_right(self, count=None):
|
||||||
|
"""Scrolls the current tab right"""
|
||||||
if count is None:
|
if count is None:
|
||||||
count = 50
|
count = 50
|
||||||
self.currentWidget().page().mainFrame().scroll(count, 0)
|
self.currentWidget().page().mainFrame().scroll(count, 0)
|
||||||
|
|
||||||
def scroll_start_act(self):
|
def cur_scroll_start(self):
|
||||||
|
"""Scrolls the current tab to the beginning"""
|
||||||
frame = self.currentWidget().page().mainFrame()
|
frame = self.currentWidget().page().mainFrame()
|
||||||
cur_pos = frame.scrollPosition()
|
cur_pos = frame.scrollPosition()
|
||||||
frame.setScrollPosition(QPoint(cur_pos.x(), 0))
|
frame.setScrollPosition(QPoint(cur_pos.x(), 0))
|
||||||
|
|
||||||
def scroll_end_act(self):
|
def cur_scroll_end(self):
|
||||||
|
"""Scrolls the current tab to the end"""
|
||||||
frame = self.currentWidget().page().mainFrame()
|
frame = self.currentWidget().page().mainFrame()
|
||||||
cur_pos = frame.scrollPosition()
|
cur_pos = frame.scrollPosition()
|
||||||
size = frame.contentsSize()
|
size = frame.contentsSize()
|
||||||
frame.setScrollPosition(QPoint(cur_pos.x(), size.height()))
|
frame.setScrollPosition(QPoint(cur_pos.x(), size.height()))
|
||||||
|
|
||||||
def switch_prev(self):
|
def switch_prev(self):
|
||||||
|
"""Switches to the previous tab"""
|
||||||
idx = self.currentIndex()
|
idx = self.currentIndex()
|
||||||
if idx > 0:
|
if idx > 0:
|
||||||
self.setCurrentIndex(idx - 1)
|
self.setCurrentIndex(idx - 1)
|
||||||
@ -109,6 +131,7 @@ class TabbedBrowser(TabWidget):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def switch_next(self):
|
def switch_next(self):
|
||||||
|
"""Switches to the next tab"""
|
||||||
idx = self.currentIndex()
|
idx = self.currentIndex()
|
||||||
if idx < self.count() - 1:
|
if idx < self.count() - 1:
|
||||||
self.setCurrentIndex(idx + 1)
|
self.setCurrentIndex(idx + 1)
|
||||||
@ -116,21 +139,22 @@ class TabbedBrowser(TabWidget):
|
|||||||
# FIXME
|
# FIXME
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def progress_changed(self, prog):
|
def keyPressEvent(self, e):
|
||||||
self.filter_signals(self.cur_progress, prog)
|
self.keypress.emit(e)
|
||||||
|
super().keyPressEvent(e)
|
||||||
|
|
||||||
def load_finished(self, ok):
|
def _titleChanged_handler(self, text):
|
||||||
self.filter_signals(self.cur_load_finished, ok)
|
|
||||||
|
|
||||||
def update_title(self, text):
|
|
||||||
if text:
|
if text:
|
||||||
self.setTabText(self.indexOf(self.sender()), text)
|
self.setTabText(self.indexOf(self.sender()), text)
|
||||||
|
|
||||||
def init_title(self):
|
def _loadStarted_handler(self):
|
||||||
s = self.sender()
|
s = self.sender()
|
||||||
self.setTabText(self.indexOf(s), s.url().toString())
|
self.setTabText(self.indexOf(s), s.url().toString())
|
||||||
|
|
||||||
def filter_signals(self, signal, *args):
|
def _filter_signals(self, signal, *args):
|
||||||
|
"""Filters signals, and triggers TabbedBrowser signals if the signal
|
||||||
|
was sent from the _current_ tab and not from any other one.
|
||||||
|
"""
|
||||||
dbgstr = "{} ({})".format(
|
dbgstr = "{} ({})".format(
|
||||||
signal.signal, ','.join([str(e) for e in args]))
|
signal.signal, ','.join([str(e) for e in args]))
|
||||||
if self.currentWidget() == self.sender():
|
if self.currentWidget() == self.sender():
|
||||||
@ -139,16 +163,12 @@ class TabbedBrowser(TabWidget):
|
|||||||
else:
|
else:
|
||||||
logging.debug('{} - ignoring'.format(dbgstr))
|
logging.debug('{} - ignoring'.format(dbgstr))
|
||||||
|
|
||||||
def index_changed(self, idx):
|
def _currentChanged_handler(self, idx):
|
||||||
tab = self.widget(idx)
|
tab = self.widget(idx)
|
||||||
self.cur_progress.emit(tab.progress)
|
self.cur_progress.emit(tab.progress)
|
||||||
|
|
||||||
def keyPressEvent(self, e):
|
|
||||||
self.keypress.emit(e)
|
|
||||||
super().keyPressEvent(e)
|
|
||||||
|
|
||||||
class BrowserTab(QWebView):
|
class BrowserTab(QWebView):
|
||||||
parent = None
|
"""One browser tab in TabbedBrowser"""
|
||||||
progress = 0
|
progress = 0
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
@ -160,9 +180,10 @@ class BrowserTab(QWebView):
|
|||||||
self.show()
|
self.show()
|
||||||
|
|
||||||
def openurl(self, url):
|
def openurl(self, url):
|
||||||
|
"""Opens an URL in the browser"""
|
||||||
if not url.startswith('http://'):
|
if not url.startswith('http://'):
|
||||||
url = 'http://' + url
|
url = 'http://' + url
|
||||||
super().load(QUrl(url))
|
self.load(QUrl(url))
|
||||||
|
|
||||||
def set_progress(self, prog):
|
def set_progress(self, prog):
|
||||||
self.progress = prog
|
self.progress = prog
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget)
|
from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QWidget
|
||||||
from qutebrowser.widgets.statusbar import StatusBar
|
from qutebrowser.widgets.statusbar import StatusBar
|
||||||
from qutebrowser.widgets.browser import TabbedBrowser
|
from qutebrowser.widgets.browser import TabbedBrowser
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
|
"""The main window of QuteBrowser"""
|
||||||
|
cwidget = None
|
||||||
|
vbox = None
|
||||||
|
tabs = None
|
||||||
|
status = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setObjectName(self.__class__.__name__)
|
|
||||||
|
|
||||||
self.cwidget = QWidget(self)
|
self.cwidget = QWidget(self)
|
||||||
self.cwidget.setObjectName("cwidget")
|
|
||||||
self.setCentralWidget(self.cwidget)
|
self.setCentralWidget(self.cwidget)
|
||||||
|
|
||||||
self.vbox = QVBoxLayout(self.cwidget)
|
self.vbox = QVBoxLayout(self.cwidget)
|
||||||
self.vbox.setObjectName("vbox")
|
|
||||||
self.vbox.setContentsMargins(0, 0, 0, 0)
|
self.vbox.setContentsMargins(0, 0, 0, 0)
|
||||||
self.vbox.setSpacing(0)
|
self.vbox.setSpacing(0)
|
||||||
|
|
||||||
self.tabs = TabbedBrowser(self)
|
self.tabs = TabbedBrowser(self)
|
||||||
self.tabs.setObjectName("tabs")
|
|
||||||
self.vbox.addWidget(self.tabs)
|
self.vbox.addWidget(self.tabs)
|
||||||
|
|
||||||
self.status = StatusBar(self)
|
self.status = StatusBar(self)
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
from PyQt5.QtWidgets import QLineEdit, QHBoxLayout, QLabel, QWidget, QShortcut, QProgressBar, QSizePolicy
|
|
||||||
from PyQt5.QtCore import pyqtSignal, Qt, QSize
|
|
||||||
from PyQt5.QtGui import QValidator, QKeySequence
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from PyQt5.QtWidgets import (QLineEdit, QHBoxLayout, QLabel, QWidget,
|
||||||
|
QShortcut, QProgressBar, QSizePolicy)
|
||||||
|
from PyQt5.QtCore import pyqtSignal, Qt, QSize
|
||||||
|
from PyQt5.QtGui import QValidator, QKeySequence
|
||||||
|
|
||||||
class StatusBar(QWidget):
|
class StatusBar(QWidget):
|
||||||
has_error = False
|
"""The statusbar at the bottom of the mainwindow"""
|
||||||
parent = None
|
has_error = False # Statusbar is currently in error mode
|
||||||
|
hbox = None
|
||||||
|
cmd = None
|
||||||
|
txt = None
|
||||||
|
prog = None
|
||||||
|
|
||||||
# TODO: the statusbar should be a bit smaller
|
# TODO: the statusbar should be a bit smaller
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.parent = parent
|
|
||||||
self.setObjectName(self.__class__.__name__)
|
|
||||||
self.set_color("white", "black")
|
self.set_color("white", "black")
|
||||||
self.hbox = QHBoxLayout(self)
|
self.hbox = QHBoxLayout(self)
|
||||||
self.hbox.setObjectName("status_hbox")
|
|
||||||
self.hbox.setContentsMargins(0, 0, 0, 0)
|
self.hbox.setContentsMargins(0, 0, 0, 0)
|
||||||
self.hbox.setSpacing(0)
|
self.hbox.setSpacing(0)
|
||||||
|
|
||||||
@ -28,6 +31,9 @@ class StatusBar(QWidget):
|
|||||||
self.hbox.addWidget(self.prog)
|
self.hbox.addWidget(self.prog)
|
||||||
|
|
||||||
def set_color(self, fg, bg):
|
def set_color(self, fg, bg):
|
||||||
|
"""Sets background and foreground color of the statusbar"""
|
||||||
|
# FIXME maybe this would be easier with setColor()?
|
||||||
|
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
* {
|
* {
|
||||||
background: """ + bg + """;
|
background: """ + bg + """;
|
||||||
@ -36,23 +42,25 @@ class StatusBar(QWidget):
|
|||||||
}""")
|
}""")
|
||||||
|
|
||||||
def disp_error(self, text):
|
def disp_error(self, text):
|
||||||
|
"""Displays an error in the statusbar"""
|
||||||
self.has_error = True
|
self.has_error = True
|
||||||
self.set_color('white', 'red')
|
self.set_color('white', 'red')
|
||||||
self.txt.error = text
|
self.txt.error = text
|
||||||
|
|
||||||
def clear_error(self):
|
def clear_error(self):
|
||||||
|
"""Clears a displayed error from the status bar"""
|
||||||
if self.has_error:
|
if self.has_error:
|
||||||
self.has_error = False
|
self.has_error = False
|
||||||
self.set_color('white', 'black')
|
self.set_color('white', 'black')
|
||||||
self.txt.error = ''
|
self.txt.error = ''
|
||||||
|
|
||||||
class StatusProgress(QProgressBar):
|
class StatusProgress(QProgressBar):
|
||||||
parent = None
|
""" The progress bar part of the status bar"""
|
||||||
|
bar = None
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, bar):
|
||||||
self.parent = parent
|
self.bar = bar
|
||||||
super().__init__(parent)
|
super().__init__(bar)
|
||||||
self.setObjectName(self.__class__.__name__)
|
|
||||||
|
|
||||||
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
|
||||||
self.setTextVisible(False)
|
self.setTextVisible(False)
|
||||||
@ -70,13 +78,15 @@ class StatusProgress(QProgressBar):
|
|||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
def minimumSizeHint(self):
|
def minimumSizeHint(self):
|
||||||
status_size = self.parent.size()
|
status_size = self.bar.size()
|
||||||
return QSize(100, status_size.height())
|
return QSize(100, status_size.height())
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return self.minimumSizeHint()
|
return self.minimumSizeHint()
|
||||||
|
|
||||||
def set_progress(self, prog):
|
def set_progress(self, prog):
|
||||||
|
"""Sets the progress of the bar and shows/hides it if necessary"""
|
||||||
|
# TODO display failed loading in some meaningful way?
|
||||||
if prog == 100:
|
if prog == 100:
|
||||||
self.setValue(prog)
|
self.setValue(prog)
|
||||||
self.hide()
|
self.hide()
|
||||||
@ -88,14 +98,14 @@ class StatusProgress(QProgressBar):
|
|||||||
self.hide()
|
self.hide()
|
||||||
|
|
||||||
class StatusText(QLabel):
|
class StatusText(QLabel):
|
||||||
|
"""The text part of the status bar, composedof several 'widgets'"""
|
||||||
keystring = ''
|
keystring = ''
|
||||||
error = ''
|
error = ''
|
||||||
text = ''
|
text = ''
|
||||||
scrollperc = ''
|
scrollperc = ''
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, bar):
|
||||||
super().__init__(parent)
|
super().__init__(bar)
|
||||||
self.setObjectName(self.__class__.__name__)
|
|
||||||
self.setStyleSheet("padding-right: 1px")
|
self.setStyleSheet("padding-right: 1px")
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
@ -103,23 +113,35 @@ class StatusText(QLabel):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def set_keystring(self, s):
|
def set_keystring(self, s):
|
||||||
|
"""Setter to be used as a Qt slot"""
|
||||||
self.keystring = s
|
self.keystring = s
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
super().setText(' '.join([self.keystring, self.error, self.text,
|
"""Update the text displayed"""
|
||||||
|
self.setText(' '.join([self.keystring, self.error, self.text,
|
||||||
self.scrollperc]))
|
self.scrollperc]))
|
||||||
|
|
||||||
class StatusCommand(QLineEdit):
|
|
||||||
got_cmd = pyqtSignal(str)
|
|
||||||
parent = None
|
|
||||||
esc_pressed = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self, parent):
|
class StatusCommand(QLineEdit):
|
||||||
super().__init__(parent)
|
"""The commandline part of the statusbar"""
|
||||||
self.parent = parent
|
class CmdValidator(QValidator):
|
||||||
self.setObjectName(self.__class__.__name__)
|
"""Validator to prevent the : from getting deleted"""
|
||||||
|
def validate(self, string, pos):
|
||||||
|
if string.startswith(':'):
|
||||||
|
return (QValidator.Acceptable, string, pos)
|
||||||
|
else:
|
||||||
|
return (QValidator.Invalid, string, pos)
|
||||||
|
|
||||||
|
got_cmd = pyqtSignal(str) # Emitted when a command is triggered by the user
|
||||||
|
bar = None # The status bar object
|
||||||
|
esc_pressed = pyqtSignal() # Emitted when escape is pressed
|
||||||
|
esc = None # The esc QShortcut object
|
||||||
|
|
||||||
|
def __init__(self, bar):
|
||||||
|
super().__init__(bar)
|
||||||
|
self.bar = bar
|
||||||
self.setStyleSheet("border: 0px; padding-left: 1px")
|
self.setStyleSheet("border: 0px; padding-left: 1px")
|
||||||
self.setValidator(CmdValidator())
|
self.setValidator(self.CmdValidator())
|
||||||
self.returnPressed.connect(self.process_cmd)
|
self.returnPressed.connect(self.process_cmd)
|
||||||
|
|
||||||
self.esc = QShortcut(self)
|
self.esc = QShortcut(self)
|
||||||
@ -128,27 +150,25 @@ class StatusCommand(QLineEdit):
|
|||||||
self.esc.activated.connect(self.esc_pressed)
|
self.esc.activated.connect(self.esc_pressed)
|
||||||
|
|
||||||
def process_cmd(self):
|
def process_cmd(self):
|
||||||
|
"""Handle the command in the status bar"""
|
||||||
text = self.text().lstrip(':')
|
text = self.text().lstrip(':')
|
||||||
self.setText('')
|
self.setText('')
|
||||||
self.got_cmd.emit(text)
|
self.got_cmd.emit(text)
|
||||||
|
|
||||||
def set_cmd(self, text):
|
def set_cmd(self, text):
|
||||||
|
"""Preset the statusbar to some text"""
|
||||||
self.setText(text)
|
self.setText(text)
|
||||||
self.setFocus()
|
self.setFocus()
|
||||||
|
|
||||||
def focusOutEvent(self, e):
|
def focusOutEvent(self, e):
|
||||||
|
"""Clear the statusbar text if it's explicitely unfocused"""
|
||||||
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
|
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
|
||||||
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
|
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
|
||||||
self.setText('')
|
self.setText('')
|
||||||
super().focusOutEvent(e)
|
super().focusOutEvent(e)
|
||||||
|
|
||||||
def focusInEvent(self, event):
|
def focusInEvent(self, e):
|
||||||
self.parent.clear_error()
|
"""Clear error message when the statusbar is focused"""
|
||||||
super().focusInEvent(event)
|
self.bar.clear_error()
|
||||||
|
super().focusInEvent(e)
|
||||||
|
|
||||||
class CmdValidator(QValidator):
|
|
||||||
def validate(self, string, pos):
|
|
||||||
if string.startswith(':'):
|
|
||||||
return (QValidator.Acceptable, string, pos)
|
|
||||||
else:
|
|
||||||
return (QValidator.Invalid, string, pos)
|
|
||||||
|
@ -2,9 +2,9 @@ from PyQt5.QtWidgets import QTabWidget
|
|||||||
from PyQt5.QtCore import Qt
|
from PyQt5.QtCore import Qt
|
||||||
|
|
||||||
class TabWidget(QTabWidget):
|
class TabWidget(QTabWidget):
|
||||||
|
"""The tabwidget used for TabbedBrowser"""
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setObjectName(self.__class__.__name__)
|
|
||||||
self.setStyleSheet("""
|
self.setStyleSheet("""
|
||||||
QTabWidget::pane {
|
QTabWidget::pane {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
Loading…
Reference in New Issue
Block a user