UI for advanced server settings

This commit is contained in:
Kovid Goyal 2017-04-11 17:54:12 +05:30
parent 968cfcbcc5
commit a4454fa151
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 172 additions and 12 deletions

View File

@ -2,12 +2,14 @@
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
# License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>
import textwrap
import time
from PyQt5.Qt import (
QCheckBox, QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout, QLabel,
QPlainTextEdit, QPushButton, QSize, QSpinBox, Qt, QTabWidget, QTimer, QUrl,
QVBoxLayout, QWidget, pyqtSignal
QCheckBox, QComboBox, QDialog, QDialogButtonBox, QDoubleSpinBox, QFormLayout,
QHBoxLayout, QLabel, QLineEdit, QPlainTextEdit, QPushButton, QScrollArea, QSize,
QSizePolicy, QSpinBox, Qt, QTabWidget, QTimer, QUrl, QVBoxLayout, QWidget,
pyqtSignal
)
from calibre import as_unicode
@ -16,7 +18,155 @@ from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.srv.opts import change_settings, options, server_config
class MainTab(QWidget):
# Advanced {{{
def init_opt(widget, opt, layout):
widget.name, widget.default_val = opt.name, opt.default
if opt.longdoc:
widget.setWhatsThis(opt.longdoc)
widget.setStatusTip(opt.longdoc)
widget.setToolTip(textwrap.fill(opt.longdoc))
layout.addRow(opt.shortdoc + ':', widget)
class Bool(QCheckBox):
changed_signal = pyqtSignal()
def __init__(self, name, layout):
opt = options[name]
QCheckBox.__init__(self)
self.stateChanged.connect(self.changed_signal.emit)
init_opt(self, opt, layout)
def get(self):
return self.isChecked()
def set(self, val):
self.setChecked(bool(val))
class Int(QSpinBox):
changed_signal = pyqtSignal()
def __init__(self, name, layout):
QSpinBox.__init__(self)
self.setRange(0, 10000)
opt = options[name]
self.valueChanged.connect(self.changed_signal.emit)
init_opt(self, opt, layout)
def get(self):
return self.value()
def set(self, val):
self.setValue(int(val))
class Float(QDoubleSpinBox):
changed_signal = pyqtSignal()
def __init__(self, name, layout):
QDoubleSpinBox.__init__(self)
self.setRange(0, 10000)
opt = options[name]
self.valueChanged.connect(self.changed_signal.emit)
init_opt(self, opt, layout)
def get(self):
return self.value()
def set(self, val):
self.setValue(float(val))
class Text(QLineEdit):
changed_signal = pyqtSignal()
def __init__(self, name, layout):
QLineEdit.__init__(self)
opt = options[name]
self.textChanged.connect(self.changed_signal.emit)
init_opt(self, opt, layout)
def get(self):
return self.text().strip() or None
def set(self, val):
self.setText(type(u'')(val or ''))
class Choices(QComboBox):
changed_signal = pyqtSignal()
def __init__(self, name, layout):
QComboBox.__init__(self)
self.setEditable(False)
opt = options[name]
self.choices = opt.choices
tuple(map(self.addItem, opt.choices))
self.currentIndexChanged.connect(self.changed_signal.emit)
init_opt(self, opt, layout)
def get(self):
return self.currentText()
def set(self, val):
if val in self.choices:
self.setCurrentText(val)
else:
self.setCurrentIndex(0)
class AdvancedTab(QWidget):
changed_signal = pyqtSignal()
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.l = l = QFormLayout(self)
l.setFieldGrowthPolicy(l.AllNonFixedFieldsGrow)
self.widgets = []
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
for name in sorted(options, key=lambda n:options[n].shortdoc.lower()):
if name in ('auth', 'port', 'allow_socket_preallocation'):
continue
opt = options[name]
if opt.choices:
w = Choices
elif isinstance(opt.default, bool):
w = Bool
elif isinstance(opt.default, (int, long)):
w = Int
elif isinstance(opt.default, float):
w = Float
else:
w = Text
w = w(name, l)
setattr(self, 'opt_' + name, w)
self.widgets.append(w)
def genesis(self):
opts = server_config()
for w in self.widgets:
w.set(getattr(opts, w.name))
w.changed_signal.connect(self.changed_signal.emit)
def restore_defaults(self):
for w in self.widgets:
w.set(w.default_val)
@property
def settings(self):
return {w.name:w.get() for w in self.widgets}
# }}}
class MainTab(QWidget): # {{{
changed_signal = pyqtSignal()
start_server = pyqtSignal()
@ -102,6 +252,7 @@ class MainTab(QWidget):
@property
def settings(self):
return {'auth': self.opt_auth.isChecked(), 'port': self.opt_port.value()}
# }}}
class ConfigWidget(ConfigWidgetBase):
@ -113,19 +264,27 @@ class ConfigWidget(ConfigWidgetBase):
self.tabs_widget = t = QTabWidget(self)
l.addWidget(t)
self.main_tab = m = MainTab(self)
t.addTab(m, _('Main'))
t.addTab(m, _('&Main'))
m.start_server.connect(self.start_server)
m.stop_server.connect(self.stop_server)
m.test_server.connect(self.test_server)
m.show_logs.connect(self.view_server_logs)
self.opt_autolaunch_server = m.opt_autolaunch_server
self.advanced_tab = a = AdvancedTab(self)
sa = QScrollArea(self)
sa.setWidget(a), sa.setWidgetResizable(True)
t.addTab(sa, _('&Advanced'))
for tab in self.tabs:
if hasattr(tab, 'changed_signal'):
tab.changed_signal.connect(self.changed_signal.emit)
@property
def tabs(self):
return (self.tabs_widget.widget(i) for i in range(self.tabs_widget.count()))
def w(x):
if isinstance(x, QScrollArea):
x = x.widget()
return x
return (w(self.tabs_widget.widget(i)) for i in range(self.tabs_widget.count()))
@property
def server(self):
@ -214,7 +373,6 @@ class ConfigWidget(ConfigWidgetBase):
for tab in self.tabs:
settings.update(getattr(tab, 'settings', {}))
change_settings(**settings)
# TODO: validate settings
def commit(self):
self.save_changes()

View File

@ -44,7 +44,7 @@ raw_options = (
'shutdown_timeout', 5.0,
None,
_('Enable/disable socket pre-allocation, for example, with systemd socket activation'),
_('Socket pre-allocation, for example, with systemd socket activation'),
'allow_socket_preallocation', True,
None,
@ -111,7 +111,7 @@ raw_options = (
' the listen_on option, then it will try to detect an interface that connects'
' to the outside world and bind to that.'),
_('Enable/disable zero copy file transfers for increased performance'),
_('Zero copy file transfers for increased performance'),
'use_sendfile', True,
_('This will use zero-copy in-kernel transfers when sending files over the network,'
' increasing performance. However, it can cause corrupted file transfers on some'
@ -122,13 +122,13 @@ raw_options = (
_('The maximum size of log files, generated by the server. When the log becomes larger'
' than this size, it is automatically rotated. Set to zero to disable log rotation.'),
_('Enable/disable logging of not found http requests'),
_('Log HTTP 404 (Not Found) requests'),
'log_not_found', True,
_('By default, the server logs all HTTP requests for resources that are not found.'
' This can generate a lot of log spam, if your server is targeted by bots.'
' Use this option to turn it off.'),
_('Enable/disable password based authentication to access the server'),
_('Password based authentication to access the server'),
'auth', False,
_('By default, the server is unrestricted, allowing anyone to access it. You can'
' restrict access to predefined users with this option.'),
@ -243,7 +243,9 @@ def parse_config_file(path=DEFAULT_CONFIG):
if opt is None:
continue
val = rest
if isinstance(opt.default, (int, long, float)):
if isinstance(opt.default, bool):
val = val.lower() in ('true', 'yes', 'y')
elif isinstance(opt.default, (int, long, float)):
try:
val = type(opt.default)(rest)
except Exception: