mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
UI for advanced server settings
This commit is contained in:
parent
968cfcbcc5
commit
a4454fa151
@ -2,12 +2,14 @@
|
|||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||||
# License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
import textwrap
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QCheckBox, QDialog, QDialogButtonBox, QFormLayout, QHBoxLayout, QLabel,
|
QCheckBox, QComboBox, QDialog, QDialogButtonBox, QDoubleSpinBox, QFormLayout,
|
||||||
QPlainTextEdit, QPushButton, QSize, QSpinBox, Qt, QTabWidget, QTimer, QUrl,
|
QHBoxLayout, QLabel, QLineEdit, QPlainTextEdit, QPushButton, QScrollArea, QSize,
|
||||||
QVBoxLayout, QWidget, pyqtSignal
|
QSizePolicy, QSpinBox, Qt, QTabWidget, QTimer, QUrl, QVBoxLayout, QWidget,
|
||||||
|
pyqtSignal
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre import as_unicode
|
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
|
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()
|
changed_signal = pyqtSignal()
|
||||||
start_server = pyqtSignal()
|
start_server = pyqtSignal()
|
||||||
@ -102,6 +252,7 @@ class MainTab(QWidget):
|
|||||||
@property
|
@property
|
||||||
def settings(self):
|
def settings(self):
|
||||||
return {'auth': self.opt_auth.isChecked(), 'port': self.opt_port.value()}
|
return {'auth': self.opt_auth.isChecked(), 'port': self.opt_port.value()}
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class ConfigWidget(ConfigWidgetBase):
|
class ConfigWidget(ConfigWidgetBase):
|
||||||
@ -113,19 +264,27 @@ class ConfigWidget(ConfigWidgetBase):
|
|||||||
self.tabs_widget = t = QTabWidget(self)
|
self.tabs_widget = t = QTabWidget(self)
|
||||||
l.addWidget(t)
|
l.addWidget(t)
|
||||||
self.main_tab = m = MainTab(self)
|
self.main_tab = m = MainTab(self)
|
||||||
t.addTab(m, _('Main'))
|
t.addTab(m, _('&Main'))
|
||||||
m.start_server.connect(self.start_server)
|
m.start_server.connect(self.start_server)
|
||||||
m.stop_server.connect(self.stop_server)
|
m.stop_server.connect(self.stop_server)
|
||||||
m.test_server.connect(self.test_server)
|
m.test_server.connect(self.test_server)
|
||||||
m.show_logs.connect(self.view_server_logs)
|
m.show_logs.connect(self.view_server_logs)
|
||||||
self.opt_autolaunch_server = m.opt_autolaunch_server
|
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:
|
for tab in self.tabs:
|
||||||
if hasattr(tab, 'changed_signal'):
|
if hasattr(tab, 'changed_signal'):
|
||||||
tab.changed_signal.connect(self.changed_signal.emit)
|
tab.changed_signal.connect(self.changed_signal.emit)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tabs(self):
|
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
|
@property
|
||||||
def server(self):
|
def server(self):
|
||||||
@ -214,7 +373,6 @@ class ConfigWidget(ConfigWidgetBase):
|
|||||||
for tab in self.tabs:
|
for tab in self.tabs:
|
||||||
settings.update(getattr(tab, 'settings', {}))
|
settings.update(getattr(tab, 'settings', {}))
|
||||||
change_settings(**settings)
|
change_settings(**settings)
|
||||||
# TODO: validate settings
|
|
||||||
|
|
||||||
def commit(self):
|
def commit(self):
|
||||||
self.save_changes()
|
self.save_changes()
|
||||||
|
@ -44,7 +44,7 @@ raw_options = (
|
|||||||
'shutdown_timeout', 5.0,
|
'shutdown_timeout', 5.0,
|
||||||
None,
|
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,
|
'allow_socket_preallocation', True,
|
||||||
None,
|
None,
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ raw_options = (
|
|||||||
' the listen_on option, then it will try to detect an interface that connects'
|
' the listen_on option, then it will try to detect an interface that connects'
|
||||||
' to the outside world and bind to that.'),
|
' 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,
|
'use_sendfile', True,
|
||||||
_('This will use zero-copy in-kernel transfers when sending files over the network,'
|
_('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'
|
' 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'
|
_('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.'),
|
' 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,
|
'log_not_found', True,
|
||||||
_('By default, the server logs all HTTP requests for resources that are not found.'
|
_('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.'
|
' This can generate a lot of log spam, if your server is targeted by bots.'
|
||||||
' Use this option to turn it off.'),
|
' 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,
|
'auth', False,
|
||||||
_('By default, the server is unrestricted, allowing anyone to access it. You can'
|
_('By default, the server is unrestricted, allowing anyone to access it. You can'
|
||||||
' restrict access to predefined users with this option.'),
|
' restrict access to predefined users with this option.'),
|
||||||
@ -243,7 +243,9 @@ def parse_config_file(path=DEFAULT_CONFIG):
|
|||||||
if opt is None:
|
if opt is None:
|
||||||
continue
|
continue
|
||||||
val = rest
|
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:
|
try:
|
||||||
val = type(opt.default)(rest)
|
val = type(opt.default)(rest)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user