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:
Florian Bruhin 2014-01-20 15:58:49 +01:00
parent dfe7d6c7ef
commit 1095e24f98
8 changed files with 301 additions and 185 deletions

View File

@ -1,16 +1,27 @@
import sys
import argparse
import logging
import signal
from PyQt5.QtWidgets import QWidget, QApplication
from signal import signal, SIGINT
from argparse import ArgumentParser
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QUrl, QTimer
import qutebrowser.commands.utils as cmdutils
from qutebrowser.widgets.mainwindow import MainWindow
from qutebrowser.commands.keys import KeyParser
from qutebrowser.utils.config import Config
import qutebrowser.commands.utils as cmdutils
from qutebrowser.utils.appdirs import AppDirs
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):
super().__init__(sys.argv)
@ -24,14 +35,15 @@ class QuteBrowser(QApplication):
self.commandparser = cmdutils.CommandParser()
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.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.mainwindow.tabs.setFocus)
self.mainwindow.status.cmd.got_cmd.connect(
self.mainwindow.tabs.setFocus)
self.commandparser.error.connect(self.mainwindow.status.disp_error)
self.keyparser.keystring_updated.connect(
self.mainwindow.status.txt.set_keystring)
self.mainwindow.status.txt.set_keystring)
self.init_cmds()
self.mainwindow.show()
@ -39,41 +51,37 @@ class QuteBrowser(QApplication):
self.python_hacks()
def python_hacks(self):
qapp = super(QApplication, self)
### Make python exceptions work
"""Gets around some PyQt-oddities by evil hacks"""
## Make python exceptions work
sys._excepthook = sys.excepthook
def exception_hook(exctype, value, traceback):
sys._excepthook(exctype, value, traceback)
# FIXME save open tabs here
qapp.exit(1)
self.exit(1)
sys.excepthook = exception_hook
### Quit on SIGINT
signal.signal(signal.SIGINT, lambda *args:
qapp.exit(128 + signal.SIGINT))
## Quit on SIGINT
signal(SIGINT, lambda *args: self.exit(128 + SIGINT))
### hack to make Ctrl+C work by passing control to the Python
### interpreter once all 500ms (lambda to ignore args)
## hack to make Ctrl+C work by passing control to the Python
## interpreter once all 500ms (lambda to ignore args)
self.timer = QTimer()
self.timer.start(500)
self.timer.timeout.connect(lambda: None)
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',
help='Set loglevel', default=0)
help='Set loglevel', default='info')
self.args = parser.parse_args()
def initlog(self):
""" Initialisation of the log """
if self.args.loglevel:
loglevel = self.args.loglevel
else:
loglevel = 'info'
"""Initialisation of the log"""
loglevel = self.args.loglevel
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
raise ValueError('Invalid log level: %s' % loglevel)
raise ValueError('Invalid log level: {}'.format(loglevel))
logging.basicConfig(
level=numeric_level,
format='%(asctime)s [%(levelname)s] '
@ -81,55 +89,63 @@ class QuteBrowser(QApplication):
datefmt='%Y-%m-%d %H:%M:%S')
def init_cmds(self):
"""Initialisation of the qutebrowser commands"""
cmdutils.register_all()
cmds = cmdutils.cmd_dict
for cmd in cmds.values():
for cmd in cmdutils.cmd_dict.values():
cmd.signal.connect(self.cmd_handler)
self.keyparser.from_config_sect(self.config['keybind'])
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
cmd = argv[0]
args = argv[1:]
handlers = {
'open': self.mainwindow.tabs.openurl,
'tabopen': self.mainwindow.tabs.tabopen,
'quit': super().quit,
'tabclose': self.mainwindow.tabs.close_act,
'tabprev': self.mainwindow.tabs.switch_prev,
'tabnext': self.mainwindow.tabs.switch_next,
'reload': self.mainwindow.tabs.reload_act,
'stop': self.mainwindow.tabs.stop_act,
'back': self.mainwindow.tabs.back_act,
'forward': self.mainwindow.tabs.forward_act,
'print': self.mainwindow.tabs.print_act,
'scrolldown': self.mainwindow.tabs.scroll_down_act,
'scrollup': self.mainwindow.tabs.scroll_up_act,
'scrollleft': self.mainwindow.tabs.scroll_left_act,
'scrollright': self.mainwindow.tabs.scroll_right_act,
'scrollstart': self.mainwindow.tabs.scroll_start_act,
'scrollend': self.mainwindow.tabs.scroll_end_act,
'undo': self.mainwindow.tabs.undo_close,
'pyeval': self.pyeval
'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,
'scrolldown': self.mainwindow.tabs.cur_scroll_down,
'scrollup': self.mainwindow.tabs.cur_scroll_up,
'scrollleft': self.mainwindow.tabs.cur_scroll_left,
'scrollright': self.mainwindow.tabs.cur_scroll_right,
'scrollstart': self.mainwindow.tabs.cur_scroll_start,
'scrollend': self.mainwindow.tabs.cur_scroll_end,
'undo': self.mainwindow.tabs.undo_close,
'pyeval': self.pyeval,
}
handler = handlers[cmd]
sender = self.sender()
if sender.count:
if self.sender().count:
handler(*args, count=count)
else:
handler(*args)
def pyeval(self, s):
"""Evaluates a python string, handler for the pyeval command"""
try:
r = eval(s)
out = repr(r)
except Exception as 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.setUrl(QUrl('about:pyeval'))
tab.setContent(out.encode('UTF-8'), 'text/plain')

View File

@ -1,6 +1,19 @@
from PyQt5.QtCore import pyqtSignal
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):
nargs = 1
split_args = False

View File

@ -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 re
from PyQt5.QtCore import QObject, pyqtSignal
import qutebrowser.commands.utils as cmdutils
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)
# Signal emitted when the keystring is updated
keystring_updated = pyqtSignal(str)
# Keybindings
key_to_cmd = {}
MATCH_PARTIAL = 0
MATCH_DEFINITIVE = 1
MATCH_NONE = 2
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():
logging.debug('registered: {} -> {}'.format(key, cmd))
logging.debug('registered key: {} -> {}'.format(key, cmd))
self.key_to_cmd[key] = cmdutils.cmd_dict[cmd]
def handle(self, e):
"""Wrapper for _handle to emit keystring_updated after _handle"""
self._handle(e)
self.keystring_updated.emit(self.keystring)
def _handle(self, e):
"""Handle a new keypress.
e -- the KeyPressEvent from Qt
"""
logging.debug('Got key: {} / text: "{}"'.format(e.key(), e.text()))
txt = e.text().strip()
if not txt:
@ -44,18 +61,20 @@ class KeyParser(QObject):
# If a keychain is ambigious, we probably should set up a QTimer with a
# configurable timeout, which triggers cmd.run() when expiring. Then
# when we enter _handle() again in time we stop the timer.
try:
cmd = self.key_to_cmd[cmdstr]
except KeyError:
if self._partial_match(cmdstr, txt):
logging.debug('No match for "{}" (added {})'.format(
self.keystring, txt))
return
else:
logging.debug('Giving up with "{}", no matches'.format(
self.keystring))
self.keystring = ''
return
(match, cmd) = self._match_key(cmdstr)
if match == self.MATCH_DEFINITIVE:
pass
elif match == self.MATCH_PARTIAL:
logging.debug('No match for "{}" (added {})'.format(self.keystring,
txt))
return
elif match == self.MATCH_NONE:
logging.debug('Giving up with "{}", no matches'.format(
self.keystring))
self.keystring = ''
return
self.keystring = ''
count = int(countstr) if countstr else None
@ -69,13 +88,19 @@ class KeyParser(QObject):
else:
cmd.run()
def _partial_match(self, cmdstr, txt):
pos = len(cmdstr)
for cmd in self.key_to_cmd:
try:
if cmdstr[-1] == cmd[pos-1]:
return True
except IndexError:
continue
else:
return False
def _match_key(self, 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:
try:
if cmdstr[-1] == cmd[len(cmdstr) - 1]:
return (self.MATCH_PARTIAL, None)
except IndexError:
# current cmd is shorter than our cmdstr, so it won't match
continue
# no definitive and no partial matches if we arrived here
return (self.MATCH_NONE, None)

View File

@ -1,31 +1,37 @@
"""Various command utils and the Command base class"""
import inspect
import sys
import logging
import shlex
from PyQt5.QtCore import QObject, pyqtSignal
cmd_dict = {}
def register_all():
import qutebrowser.commands.commands
def is_cmd(obj):
return (inspect.isclass(obj) and
obj.__module__ == 'qutebrowser.commands.commands')
class ArgumentCountError(TypeError):
pass
for (name, cls) in inspect.getmembers(qutebrowser.commands.commands,
is_cmd):
if cls.bind:
obj = cls()
cmd_dict[obj.name] = obj
def register_all():
"""Register and initialize all commands."""
# 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()
cmd_dict[obj.name] = obj
class CommandParser(QObject):
error = pyqtSignal(str)
"""Parser for qutebrowser commandline commands"""
error = pyqtSignal(str) # Emitted if there's an error
def parse(self, text):
"""Parses a command and runs its handler"""
parts = text.strip().split(maxsplit=1)
# FIXME maybe we should handle unambigious shorthands for commands here?
# Or at least we should add :q for :quit.
# FIXME maybe we should handle unambigious shorthands for commands
# here? Or at least we should add :q for :quit.
cmd = parts[0]
try:
obj = cmd_dict[cmd]
@ -42,17 +48,19 @@ class CommandParser(QObject):
try:
obj.check(args)
except TypeError:
except ArgumentCountError:
self.error.emit("{}: invalid argument count".format(cmd))
return
obj.run(args)
class Command(QObject):
"""Base skeleton for a command. See the module help for
qutebrowser.commands.commands for details.
"""
nargs = 0
name = None
signal = None
count = False
bind = True
split_args = True
signal = pyqtSignal(tuple)
@ -62,16 +70,28 @@ class Command(QObject):
self.name = self.__class__.__name__.lower()
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
(self.nargs == '?' and len(args) > 1) or
(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):
countstr = ' * {}'.format(count) if count is not None else ''
argstr = ", ".join(args) if args else ''
logging.debug("Cmd called: {}({}){}".format(self.name, argstr,
countstr))
"""Runs the command.
args -- Arguments to the command.
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]
if args is not None:
argv += args

View File

@ -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
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):
cur_progress = pyqtSignal(int)
cur_load_finished = pyqtSignal(bool)
"""A TabWidget with QWebViews inside"""
cur_progress = pyqtSignal(int) # Progress of the current tab changed
cur_load_finished = pyqtSignal(bool) # Current tab finished loading
keypress = pyqtSignal('QKeyEvent')
url_stack = []
_url_stack = [] # Stack of URLs of closed tabs
def __init__(self, parent):
super().__init__(parent)
self.currentChanged.connect(self.index_changed)
self.currentChanged.connect(self._currentChanged_handler)
self.tabopen("http://ddg.gg/")
def tabopen(self, url):
"""Opens a new tab with a given url"""
tab = BrowserTab(self)
tab.openurl(url)
self.addTab(tab, url)
self.setCurrentWidget(tab)
self.progress_changed(tab.progress)
tab.loadProgress.connect(self.progress_changed)
tab.loadFinished.connect(self.load_finished)
self.cur_progress.emit(tab.progress)
tab.loadProgress.connect(
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
# isn't set correctly at this point, e.g. when doing
# setContent(..., baseUrl=QUrl('foo'))
tab.loadStarted.connect(self.init_title)
tab.titleChanged.connect(self.update_title)
tab.loadStarted.connect(self._loadStarted_handler)
tab.titleChanged.connect(self._titleChanged_handler)
def openurl(self, url):
"""Opens an url in the current tab"""
tab = self.currentWidget()
tab.openurl(url)
def undo_close(self):
if self.url_stack:
self.tabopen(self.url_stack.pop())
"""Undos closing a tab"""
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:
idx = self.currentIndex()
tab = self.currentWidget()
# FIXME maybe we should add the QUrl object here and deal with QUrls everywhere
# 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)
else:
# FIXME
pass
def reload_act(self):
def cur_reload(self):
"""Reloads the current tab"""
self.currentWidget().reload()
def stop_act(self):
def cur_stop(self):
"""Stops loading in the current tab"""
self.currentWidget().stop()
def print_act(self):
def cur_print(self):
"""Prints the current tab"""
# FIXME that does not what I expect
preview = QPrintPreviewDialog()
preview.paintRequested.connect(self.currentWidget().print)
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
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
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:
count = 50
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:
count = 50
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:
count = 50
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:
count = 50
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()
cur_pos = frame.scrollPosition()
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()
cur_pos = frame.scrollPosition()
size = frame.contentsSize()
frame.setScrollPosition(QPoint(cur_pos.x(), size.height()))
def switch_prev(self):
"""Switches to the previous tab"""
idx = self.currentIndex()
if idx > 0:
self.setCurrentIndex(idx - 1)
@ -109,6 +131,7 @@ class TabbedBrowser(TabWidget):
pass
def switch_next(self):
"""Switches to the next tab"""
idx = self.currentIndex()
if idx < self.count() - 1:
self.setCurrentIndex(idx + 1)
@ -116,21 +139,22 @@ class TabbedBrowser(TabWidget):
# FIXME
pass
def progress_changed(self, prog):
self.filter_signals(self.cur_progress, prog)
def keyPressEvent(self, e):
self.keypress.emit(e)
super().keyPressEvent(e)
def load_finished(self, ok):
self.filter_signals(self.cur_load_finished, ok)
def update_title(self, text):
def _titleChanged_handler(self, text):
if text:
self.setTabText(self.indexOf(self.sender()), text)
def init_title(self):
def _loadStarted_handler(self):
s = self.sender()
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(
signal.signal, ','.join([str(e) for e in args]))
if self.currentWidget() == self.sender():
@ -139,16 +163,12 @@ class TabbedBrowser(TabWidget):
else:
logging.debug('{} - ignoring'.format(dbgstr))
def index_changed(self, idx):
def _currentChanged_handler(self, idx):
tab = self.widget(idx)
self.cur_progress.emit(tab.progress)
def keyPressEvent(self, e):
self.keypress.emit(e)
super().keyPressEvent(e)
class BrowserTab(QWebView):
parent = None
"""One browser tab in TabbedBrowser"""
progress = 0
def __init__(self, parent):
@ -160,9 +180,10 @@ class BrowserTab(QWebView):
self.show()
def openurl(self, url):
"""Opens an URL in the browser"""
if not url.startswith('http://'):
url = 'http://' + url
super().load(QUrl(url))
self.load(QUrl(url))
def set_progress(self, prog):
self.progress = prog

View File

@ -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.browser import TabbedBrowser
class MainWindow(QMainWindow):
"""The main window of QuteBrowser"""
cwidget = None
vbox = None
tabs = None
status = None
def __init__(self):
super().__init__()
self.setObjectName(self.__class__.__name__)
self.cwidget = QWidget(self)
self.cwidget.setObjectName("cwidget")
self.setCentralWidget(self.cwidget)
self.vbox = QVBoxLayout(self.cwidget)
self.vbox.setObjectName("vbox")
self.vbox.setContentsMargins(0, 0, 0, 0)
self.vbox.setSpacing(0)
self.tabs = TabbedBrowser(self)
self.tabs.setObjectName("tabs")
self.vbox.addWidget(self.tabs)
self.status = StatusBar(self)

View File

@ -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
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):
has_error = False
parent = None
"""The statusbar at the bottom of the mainwindow"""
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
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.setObjectName(self.__class__.__name__)
self.set_color("white", "black")
self.hbox = QHBoxLayout(self)
self.hbox.setObjectName("status_hbox")
self.hbox.setContentsMargins(0, 0, 0, 0)
self.hbox.setSpacing(0)
@ -28,6 +31,9 @@ class StatusBar(QWidget):
self.hbox.addWidget(self.prog)
def set_color(self, fg, bg):
"""Sets background and foreground color of the statusbar"""
# FIXME maybe this would be easier with setColor()?
self.setStyleSheet("""
* {
background: """ + bg + """;
@ -36,23 +42,25 @@ class StatusBar(QWidget):
}""")
def disp_error(self, text):
"""Displays an error in the statusbar"""
self.has_error = True
self.set_color('white', 'red')
self.txt.error = text
def clear_error(self):
"""Clears a displayed error from the status bar"""
if self.has_error:
self.has_error = False
self.set_color('white', 'black')
self.txt.error = ''
class StatusProgress(QProgressBar):
parent = None
""" The progress bar part of the status bar"""
bar = None
def __init__(self, parent):
self.parent = parent
super().__init__(parent)
self.setObjectName(self.__class__.__name__)
def __init__(self, bar):
self.bar = bar
super().__init__(bar)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setTextVisible(False)
@ -70,13 +78,15 @@ class StatusProgress(QProgressBar):
self.hide()
def minimumSizeHint(self):
status_size = self.parent.size()
status_size = self.bar.size()
return QSize(100, status_size.height())
def sizeHint(self):
return self.minimumSizeHint()
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:
self.setValue(prog)
self.hide()
@ -88,14 +98,14 @@ class StatusProgress(QProgressBar):
self.hide()
class StatusText(QLabel):
"""The text part of the status bar, composedof several 'widgets'"""
keystring = ''
error = ''
text = ''
scrollperc = ''
def __init__(self, parent):
super().__init__(parent)
self.setObjectName(self.__class__.__name__)
def __init__(self, bar):
super().__init__(bar)
self.setStyleSheet("padding-right: 1px")
def __setattr__(self, name, value):
@ -103,23 +113,35 @@ class StatusText(QLabel):
self.update()
def set_keystring(self, s):
"""Setter to be used as a Qt slot"""
self.keystring = s
def update(self):
super().setText(' '.join([self.keystring, self.error, self.text,
self.scrollperc]))
"""Update the text displayed"""
self.setText(' '.join([self.keystring, self.error, self.text,
self.scrollperc]))
class StatusCommand(QLineEdit):
got_cmd = pyqtSignal(str)
parent = None
esc_pressed = pyqtSignal()
"""The commandline part of the statusbar"""
class CmdValidator(QValidator):
"""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)
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.setObjectName(self.__class__.__name__)
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.setValidator(CmdValidator())
self.setValidator(self.CmdValidator())
self.returnPressed.connect(self.process_cmd)
self.esc = QShortcut(self)
@ -128,27 +150,25 @@ class StatusCommand(QLineEdit):
self.esc.activated.connect(self.esc_pressed)
def process_cmd(self):
"""Handle the command in the status bar"""
text = self.text().lstrip(':')
self.setText('')
self.got_cmd.emit(text)
def set_cmd(self, text):
"""Preset the statusbar to some text"""
self.setText(text)
self.setFocus()
def focusOutEvent(self, e):
"""Clear the statusbar text if it's explicitely unfocused"""
if e.reason() in [Qt.MouseFocusReason, Qt.TabFocusReason,
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
Qt.BacktabFocusReason, Qt.OtherFocusReason]:
self.setText('')
super().focusOutEvent(e)
def focusInEvent(self, event):
self.parent.clear_error()
super().focusInEvent(event)
def focusInEvent(self, e):
"""Clear error message when the statusbar is focused"""
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)

View File

@ -2,9 +2,9 @@ from PyQt5.QtWidgets import QTabWidget
from PyQt5.QtCore import Qt
class TabWidget(QTabWidget):
"""The tabwidget used for TabbedBrowser"""
def __init__(self, parent):
super().__init__(parent)
self.setObjectName(self.__class__.__name__)
self.setStyleSheet("""
QTabWidget::pane {
position: absolute;