mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
GUI for creating custom list templates
This commit is contained in:
parent
72c82aeb05
commit
e7af7af508
@ -2,6 +2,8 @@
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
# License: GPLv3 Copyright: 2010, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
import textwrap
|
||||
import time
|
||||
@ -19,6 +21,8 @@ from calibre.gui2 import (
|
||||
config, error_dialog, gprefs, info_dialog, open_url, warning_dialog
|
||||
)
|
||||
from calibre.gui2.preferences import AbortCommit, ConfigWidgetBase, test_widget
|
||||
from calibre.srv.code import custom_list_template as default_custom_list_template
|
||||
from calibre.srv.embedded import custom_list_template
|
||||
from calibre.srv.library_broker import load_gui_libraries
|
||||
from calibre.srv.opts import change_settings, options, server_config
|
||||
from calibre.srv.users import (
|
||||
@ -26,7 +30,6 @@ from calibre.srv.users import (
|
||||
)
|
||||
from calibre.utils.icu import primary_sort_key
|
||||
|
||||
|
||||
# Advanced {{{
|
||||
|
||||
|
||||
@ -729,6 +732,104 @@ class Users(QWidget):
|
||||
# }}}
|
||||
|
||||
|
||||
class CustomList(QWidget): # {{{
|
||||
|
||||
changed_signal = pyqtSignal()
|
||||
|
||||
def __init__(self, parent):
|
||||
QWidget.__init__(self, parent)
|
||||
self.default_template = default_custom_list_template()
|
||||
self.l = l = QFormLayout(self)
|
||||
l.setFieldGrowthPolicy(l.AllNonFixedFieldsGrow)
|
||||
self.la = la = QLabel('<p>' + _(
|
||||
'Here you can create a template to control what data is shown when'
|
||||
' using the <i>Custom list</i> mode for the book list'))
|
||||
la.setWordWrap(True)
|
||||
l.addRow(la)
|
||||
self.thumbnail = t = QCheckBox(_('Show a cover &thumbnail'))
|
||||
self.thumbnail_height = th = QSpinBox(self)
|
||||
th.setSuffix(' px'), th.setRange(60, 600)
|
||||
self.entry_height = eh = QLineEdit(self)
|
||||
l.addRow(t), l.addRow(_('Thumbnail &height:'), th)
|
||||
l.addRow(_('Entry &height:'), eh)
|
||||
t.stateChanged.connect(self.changed_signal)
|
||||
th.valueChanged.connect(self.changed_signal)
|
||||
eh.textChanged.connect(self.changed_signal)
|
||||
eh.setToolTip(textwrap.fill(_(
|
||||
'The height for each entry. The special value "auto" causes a height to be calculated'
|
||||
' based on the number of lines in the template. Otherwise, use a CSS length, such as'
|
||||
' 100px or 15ex')))
|
||||
t.stateChanged.connect(self.thumbnail_state_changed)
|
||||
th.setVisible(False)
|
||||
|
||||
self.comments_fields = cf = QLineEdit(self)
|
||||
l.addRow(_('&Long text fields:'), cf)
|
||||
cf.setToolTip(textwrap.fill(_(
|
||||
'A comma separated list of fields that will be added at the bottom of every entry.'
|
||||
' These fields are interpreted as containing HTML, not plain text.')))
|
||||
cf.textChanged.connect(self.changed_signal)
|
||||
|
||||
self.la1 = la = QLabel('<p>' + _(
|
||||
'The template below will be interpreted as HTML and all {{fields}} will be replaced'
|
||||
' by the actual metadata, if available. You can use {0} as a separator'
|
||||
' to split a line into multiple columns.').format('|||'))
|
||||
la.setWordWrap(True)
|
||||
l.addRow(la)
|
||||
self.template = t = QPlainTextEdit(self)
|
||||
l.addRow(t)
|
||||
t.textChanged.connect(self.changed_signal)
|
||||
|
||||
def thumbnail_state_changed(self):
|
||||
is_enabled = bool(self.thumbnail.isChecked())
|
||||
for w, x in [(self.thumbnail_height, True), (self.entry_height, False)]:
|
||||
w.setVisible(is_enabled is x)
|
||||
self.layout().labelForField(w).setVisible(is_enabled is x)
|
||||
|
||||
def genesis(self):
|
||||
self.current_template = custom_list_template() or self.default_template
|
||||
|
||||
@property
|
||||
def current_template(self):
|
||||
return {
|
||||
'thumbnail': self.thumbnail.isChecked(),
|
||||
'thumbnail_height': self.thumbnail_height.value(),
|
||||
'height': self.entry_height.text().strip() or 'auto',
|
||||
'comments_fields': [x.strip() for x in self.comments_fields.text().split(',') if x.strip()],
|
||||
'lines': [x.strip() for x in self.template.toPlainText().splitlines()]
|
||||
}
|
||||
|
||||
@current_template.setter
|
||||
def current_template(self, template):
|
||||
self.thumbnail.setChecked(bool(template.get('thumbnail')))
|
||||
try:
|
||||
th = int(template['thumbnail_height'])
|
||||
except Exception:
|
||||
th = self.default_template['thumbnail_height']
|
||||
self.thumbnail_height.setValue(th)
|
||||
self.entry_height.setText(template.get('height') or 'auto')
|
||||
self.comments_fields.setText(', '.join(template.get('comments_fields') or ()))
|
||||
self.template.setPlainText('\n'.join(template.get('lines') or ()))
|
||||
|
||||
def restore_defaults(self):
|
||||
self.current_template = self.default_template
|
||||
|
||||
def commit(self):
|
||||
template = self.current_template
|
||||
if template == self.default_template:
|
||||
try:
|
||||
os.remove(custom_list_template.path)
|
||||
except EnvironmentError as err:
|
||||
if err.errno != errno.ENOENT:
|
||||
raise
|
||||
else:
|
||||
raw = json.dumps(template, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
with lopen(custom_list_template.path, 'wb') as f:
|
||||
f.write(raw)
|
||||
return True
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
class ConfigWidget(ConfigWidgetBase):
|
||||
|
||||
def __init__(self, *args, **kw):
|
||||
@ -750,6 +851,10 @@ class ConfigWidget(ConfigWidgetBase):
|
||||
sa = QScrollArea(self)
|
||||
sa.setWidget(a), sa.setWidgetResizable(True)
|
||||
t.addTab(sa, _('&Advanced'))
|
||||
self.custom_list_tab = clt = CustomList(self)
|
||||
sa = QScrollArea(self)
|
||||
sa.setWidget(clt), sa.setWidgetResizable(True)
|
||||
t.addTab(sa, _('Book &list template'))
|
||||
for tab in self.tabs:
|
||||
if hasattr(tab, 'changed_signal'):
|
||||
tab.changed_signal.connect(self.changed_signal.emit)
|
||||
@ -882,6 +987,8 @@ class ConfigWidget(ConfigWidgetBase):
|
||||
)
|
||||
self.tabs_widget.setCurrentWidget(self.users_tab)
|
||||
return False
|
||||
if not self.custom_list_tab.commit():
|
||||
return False
|
||||
ConfigWidgetBase.commit(self)
|
||||
change_settings(**settings)
|
||||
UserManager().user_data = users
|
||||
@ -902,6 +1009,7 @@ class ConfigWidget(ConfigWidgetBase):
|
||||
def refresh_gui(self, gui):
|
||||
if self.server:
|
||||
self.server.user_manager.refresh()
|
||||
self.server.ctx.custom_list_template = custom_list_template()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -4,11 +4,12 @@
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
from threading import Thread
|
||||
|
||||
from calibre import as_unicode
|
||||
from calibre.constants import cache_dir, is_running_from_develop
|
||||
from calibre.constants import cache_dir, config_dir, is_running_from_develop
|
||||
from calibre.srv.bonjour import BonJour
|
||||
from calibre.srv.handler import Handler
|
||||
from calibre.srv.http_response import create_http_handler
|
||||
@ -23,6 +24,20 @@ def log_paths():
|
||||
)
|
||||
|
||||
|
||||
def custom_list_template():
|
||||
try:
|
||||
with lopen(custom_list_template.path, 'rb') as f:
|
||||
raw = f.read()
|
||||
except EnvironmentError as err:
|
||||
if err.errno != errno.ENOENT:
|
||||
raise
|
||||
return
|
||||
return json.loads(raw)
|
||||
|
||||
|
||||
custom_list_template.path = os.path.join(config_dir, 'server-custom-list-template.json')
|
||||
|
||||
|
||||
class Server(object):
|
||||
|
||||
loop = current_thread = exception = None
|
||||
@ -46,6 +61,11 @@ class Server(object):
|
||||
self.opts = opts
|
||||
self.log, self.access_log = log, access_log
|
||||
self.handler.set_log(self.log)
|
||||
self.handler.router.ctx.custom_list_template = custom_list_template()
|
||||
|
||||
@property
|
||||
def ctx(self):
|
||||
return self.handler.router.ctx
|
||||
|
||||
@property
|
||||
def user_manager(self):
|
||||
|
@ -187,7 +187,7 @@ def render_part(part, template, book_id, metadata):
|
||||
if not n:
|
||||
break
|
||||
rendered = E.span()
|
||||
for field in n.nodeValue.split(/({[_a-z0-9]+})/):
|
||||
for field in n.nodeValue.split(/({#?[_a-z0-9]+})/):
|
||||
if field[0] is '{' and field[-1] is '}':
|
||||
count += 1
|
||||
val = render_field(field[1:-1], metadata, book_id)
|
||||
@ -272,7 +272,8 @@ def create_item(book_id, metadata, create_image, show_book_details):
|
||||
height = f'{template.thumbnail_height}px'
|
||||
else:
|
||||
if template.height is 'auto':
|
||||
height = (template.lines.length * 2.5 + 1) + 'ex'
|
||||
extra = 5 if template.comments_fields.length else 1
|
||||
height = (template.lines.length * 2.5 + extra) + 'ex'
|
||||
else:
|
||||
height = template.height
|
||||
if jstype(height) is 'number':
|
||||
|
Loading…
x
Reference in New Issue
Block a user