mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add field box to color wizard
This commit is contained in:
parent
f25c423261
commit
19261f15ee
@ -5,12 +5,16 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
from functools import partial
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox,
|
from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox,
|
||||||
QDialogButtonBox, QColor, QComboBox, QIcon)
|
QDialogButtonBox, QColor, QComboBox, QIcon)
|
||||||
|
|
||||||
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
from calibre.gui2.dialogs.template_dialog import TemplateDialog
|
||||||
from calibre.gui2.complete import MultiCompleteLineEdit
|
from calibre.gui2.complete import MultiCompleteLineEdit
|
||||||
from calibre.gui2 import error_dialog
|
from calibre.gui2 import error_dialog
|
||||||
|
from calibre.utils.icu import sort_key
|
||||||
|
|
||||||
class TemplateLineEditor(QLineEdit):
|
class TemplateLineEditor(QLineEdit):
|
||||||
|
|
||||||
@ -26,8 +30,8 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
def set_mi(self, mi):
|
def set_mi(self, mi):
|
||||||
self.mi = mi
|
self.mi = mi
|
||||||
|
|
||||||
def set_tags(self, tags):
|
def set_db(self, db):
|
||||||
self.tags = tags
|
self.db = db
|
||||||
|
|
||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
menu = self.createStandardContextMenu()
|
menu = self.createStandardContextMenu()
|
||||||
@ -35,9 +39,6 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
|
|
||||||
action_open_editor = menu.addAction(_('Open Template Editor'))
|
action_open_editor = menu.addAction(_('Open Template Editor'))
|
||||||
action_open_editor.triggered.connect(self.open_editor)
|
action_open_editor.triggered.connect(self.open_editor)
|
||||||
if self.tags:
|
|
||||||
action_tag_wizard = menu.addAction(_('Open Tag Wizard'))
|
|
||||||
action_tag_wizard.triggered.connect(self.tag_wizard)
|
|
||||||
menu.exec_(event.globalPos())
|
menu.exec_(event.globalPos())
|
||||||
|
|
||||||
def open_editor(self):
|
def open_editor(self):
|
||||||
@ -53,84 +54,117 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
_('The text in the box was not generated by this wizard'),
|
_('The text in the box was not generated by this wizard'),
|
||||||
show=True, show_copy_button=False)
|
show=True, show_copy_button=False)
|
||||||
return
|
return
|
||||||
d = TagWizard(self, self.tags, unicode(self.text()))
|
d = TagWizard(self, self.db, unicode(self.text()))
|
||||||
if d.exec_():
|
if d.exec_():
|
||||||
self.setText(d.template)
|
self.setText(d.template)
|
||||||
|
|
||||||
class TagWizard(QDialog):
|
class TagWizard(QDialog):
|
||||||
|
|
||||||
def __init__(self, parent, tags, txt):
|
def __init__(self, parent, db, txt):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
self.setWindowTitle(_('Tag Wizard'))
|
self.setWindowTitle(_('Coloring Wizard'))
|
||||||
self.setWindowIcon(QIcon(I('wizard.png')))
|
self.setWindowIcon(QIcon(I('wizard.png')))
|
||||||
|
|
||||||
self.tags = tags
|
self.columns = []
|
||||||
|
self.completion_values = defaultdict(dict)
|
||||||
|
for k in db.all_field_keys():
|
||||||
|
m = db.metadata_for_field(k)
|
||||||
|
if m['datatype'] in ('text', 'enumeration', 'series'):
|
||||||
|
self.columns.append(k)
|
||||||
|
if m['is_custom']:
|
||||||
|
# self.completion_values[k] = {}
|
||||||
|
self.completion_values[k]['v'] = db.all_custom(m['label'])
|
||||||
|
elif k == 'tags':
|
||||||
|
# self.completion_values[k] = {}
|
||||||
|
self.completion_values[k]['v'] = db.all_tags()
|
||||||
|
else:
|
||||||
|
f = getattr(db, 'all' + k, None)
|
||||||
|
if f:
|
||||||
|
self.completion_values[k] = {}
|
||||||
|
self.completion_values[k]['v'] = [v[1] for v in f()]
|
||||||
|
if k in self.completion_values:
|
||||||
|
self.completion_values[k]['m'] = m['is_multiple']
|
||||||
|
|
||||||
|
self.columns.sort(key=sort_key)
|
||||||
|
self.columns.insert(0, '')
|
||||||
|
|
||||||
l = QGridLayout()
|
l = QGridLayout()
|
||||||
self.setLayout(l)
|
self.setLayout(l)
|
||||||
l.setColumnStretch(0, 1)
|
l.setColumnStretch(1, 10)
|
||||||
l.setColumnMinimumWidth(0, 300)
|
l.setColumnMinimumWidth(1, 300)
|
||||||
h = QLabel(_('Tags (see the popup help for more information)'))
|
|
||||||
|
h = QLabel(_('Column'))
|
||||||
|
l.addWidget(h, 0, 0, 1, 1)
|
||||||
|
|
||||||
|
h = QLabel(_('Values (see the popup help for more information)'))
|
||||||
h.setToolTip('<p>' +
|
h.setToolTip('<p>' +
|
||||||
_('You can enter more than one tag per box, separated by commas. '
|
_('You can enter more than one value per box, separated by commas. '
|
||||||
'The comparison ignores letter case.<br>'
|
'The comparison ignores letter case.<br>'
|
||||||
'A tag value can be a regular expression. Check the box to turn '
|
'A value can be a regular expression. Check the box to turn '
|
||||||
'them on. When using regular expressions, note that the wizard '
|
'them on. When using regular expressions, note that the wizard '
|
||||||
'puts anchors (^ and $) around the expression, so you '
|
'puts anchors (^ and $) around the expression, so you '
|
||||||
'must ensure your expression matches from the beginning '
|
'must ensure your expression matches from the beginning '
|
||||||
'to the end of the tag.<br>'
|
'to the end of the column you are checking.<br>'
|
||||||
'Regular expression examples:') + '<ul>' +
|
'Regular expression examples:') + '<ul>' +
|
||||||
_('<li><code><b>.*</b></code> matches any tag. No empty tags are '
|
_('<li><code><b>.*</b></code> matches anything in the column. No '
|
||||||
'checked, so you don\'t need to worry about empty strings</li>'
|
'empty values are checked, so you don\'t need to worry about '
|
||||||
'<li><code><b>A.*</b></code> matches any tag beginning with A</li>'
|
'empty strings</li>'
|
||||||
'<li><code><b>.*mystery.*</b></code> matches any tag containing '
|
'<li><code><b>A.*</b></code> matches anything beginning with A</li>'
|
||||||
|
'<li><code><b>.*mystery.*</b></code> matches anything containing '
|
||||||
'the word "mystery"</li>') + '</ul></p>')
|
'the word "mystery"</li>') + '</ul></p>')
|
||||||
l.addWidget(h , 0, 0, 1, 1)
|
l.addWidget(h , 0, 1, 1, 1)
|
||||||
|
|
||||||
c = QLabel(_('is RE'))
|
c = QLabel(_('is RE'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('Check this box if the tag box contains regular expressions') + '</p>')
|
_('Check this box if the values box contains regular expressions') + '</p>')
|
||||||
l.addWidget(c, 0, 1, 1, 1)
|
l.addWidget(c, 0, 2, 1, 1)
|
||||||
|
|
||||||
c = QLabel(_('Color if tag found'))
|
c = QLabel(_('Color if value found'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('At least one of the two color boxes must have a value. Leave '
|
_('At least one of the two color boxes must have a value. Leave '
|
||||||
'one color box empty if you want the template to use the next '
|
'one color box empty if you want the template to use the next '
|
||||||
'line in this wizard. If both boxes are filled in, the rest of '
|
'line in this wizard. If both boxes are filled in, the rest of '
|
||||||
'the lines in this wizard will be ignored.') + '</p>')
|
'the lines in this wizard will be ignored.') + '</p>')
|
||||||
l.addWidget(c, 0, 2, 1, 1)
|
l.addWidget(c, 0, 3, 1, 1)
|
||||||
c = QLabel(_('Color if tag not found'))
|
c = QLabel(_('Color if value not found'))
|
||||||
c.setToolTip('<p>' +
|
c.setToolTip('<p>' +
|
||||||
_('This box is usually filled in only on the last test. If it is '
|
_('This box is usually filled in only on the last test. If it is '
|
||||||
'filled in before the last test, then the color for tag found box '
|
'filled in before the last test, then the color for value found box '
|
||||||
'must be empty or all the rest of the tests will be ignored.') + '</p>')
|
'must be empty or all the rest of the tests will be ignored.') + '</p>')
|
||||||
l.addWidget(c, 0, 3, 1, 1)
|
l.addWidget(c, 0, 4, 1, 1)
|
||||||
self.tagboxes = []
|
self.tagboxes = []
|
||||||
self.colorboxes = []
|
self.colorboxes = []
|
||||||
self.nfcolorboxes = []
|
self.nfcolorboxes = []
|
||||||
self.reboxes = []
|
self.reboxes = []
|
||||||
|
self.colboxes = []
|
||||||
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
self.colors.insert(0, '')
|
self.colors.insert(0, '')
|
||||||
for i in range(0, 10):
|
for i in range(0, 10):
|
||||||
|
w = QComboBox()
|
||||||
|
w.addItems(self.columns)
|
||||||
|
l.addWidget(w, i+1, 0, 1, 1)
|
||||||
|
self.colboxes.append(w)
|
||||||
|
|
||||||
tb = MultiCompleteLineEdit(self)
|
tb = MultiCompleteLineEdit(self)
|
||||||
tb.set_separator(', ')
|
tb.set_separator(', ')
|
||||||
tb.update_items_cache(self.tags)
|
|
||||||
self.tagboxes.append(tb)
|
self.tagboxes.append(tb)
|
||||||
l.addWidget(tb, i+1, 0, 1, 1)
|
l.addWidget(tb, i+1, 1, 1, 1)
|
||||||
|
w.currentIndexChanged[str].connect(partial(self.column_changed, valbox=tb))
|
||||||
|
|
||||||
w = QCheckBox(self)
|
w = QCheckBox(self)
|
||||||
self.reboxes.append(w)
|
self.reboxes.append(w)
|
||||||
l.addWidget(w, i+1, 1, 1, 1)
|
|
||||||
|
|
||||||
w = QComboBox(self)
|
|
||||||
w.addItems(self.colors)
|
|
||||||
self.colorboxes.append(w)
|
|
||||||
l.addWidget(w, i+1, 2, 1, 1)
|
l.addWidget(w, i+1, 2, 1, 1)
|
||||||
|
|
||||||
w = QComboBox(self)
|
w = QComboBox(self)
|
||||||
w.addItems(self.colors)
|
w.addItems(self.colors)
|
||||||
self.nfcolorboxes.append(w)
|
self.colorboxes.append(w)
|
||||||
l.addWidget(w, i+1, 3, 1, 1)
|
l.addWidget(w, i+1, 3, 1, 1)
|
||||||
|
|
||||||
|
w = QComboBox(self)
|
||||||
|
w.addItems(self.colors)
|
||||||
|
self.nfcolorboxes.append(w)
|
||||||
|
l.addWidget(w, i+1, 4, 1, 1)
|
||||||
|
|
||||||
if txt:
|
if txt:
|
||||||
lines = txt.split('\n')[3:]
|
lines = txt.split('\n')[3:]
|
||||||
i = 0
|
i = 0
|
||||||
@ -141,37 +175,59 @@ class TagWizard(QDialog):
|
|||||||
t, c = vals
|
t, c = vals
|
||||||
nc = ''
|
nc = ''
|
||||||
re = False
|
re = False
|
||||||
|
f = 'tags'
|
||||||
else:
|
else:
|
||||||
t,c,nc,re = vals
|
t,c,f,nc,re = vals
|
||||||
try:
|
try:
|
||||||
self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
|
self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
|
||||||
self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc))
|
self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc))
|
||||||
self.tagboxes[i].setText(t)
|
self.tagboxes[i].setText(t)
|
||||||
self.reboxes[i].setChecked(re == '2')
|
self.reboxes[i].setChecked(re == '2')
|
||||||
|
self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f))
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
||||||
l.addWidget(bb, 100, 2, 1, 2)
|
l.addWidget(bb, 100, 3, 1, 2)
|
||||||
bb.accepted.connect(self.accepted)
|
bb.accepted.connect(self.accepted)
|
||||||
bb.rejected.connect(self.reject)
|
bb.rejected.connect(self.reject)
|
||||||
self.template = ''
|
self.template = ''
|
||||||
|
|
||||||
|
def column_changed(self, s, valbox=None):
|
||||||
|
k = unicode(s)
|
||||||
|
if k in self.completion_values:
|
||||||
|
valbox.update_items_cache(self.completion_values[k]['v'])
|
||||||
|
if self.completion_values[k]['m']:
|
||||||
|
valbox.set_separator(', ')
|
||||||
|
else:
|
||||||
|
valbox.set_separator(None)
|
||||||
|
else:
|
||||||
|
valbox.update_items_cache([])
|
||||||
|
valbox.set_separator(None)
|
||||||
|
|
||||||
def accepted(self):
|
def accepted(self):
|
||||||
res = ("program:\n#tag wizard -- do not directly edit\n"
|
res = ("program:\n#tag wizard -- do not directly edit\n"
|
||||||
" t = field('tags');\n first_non_empty(\n")
|
" first_non_empty(\n")
|
||||||
lines = []
|
lines = []
|
||||||
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
||||||
self.nfcolorboxes, self.reboxes):
|
self.colboxes, self.nfcolorboxes, self.reboxes):
|
||||||
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
|
f = unicode(fb.currentText())
|
||||||
|
if not f:
|
||||||
|
continue
|
||||||
|
m = self.completion_values[f]['m']
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
nfc = unicode(nfcb.currentText()).strip()
|
||||||
re = reb.checkState()
|
re = reb.checkState()
|
||||||
if re == 2:
|
if m:
|
||||||
tags = '$|^'.join(tags)
|
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
|
||||||
|
if re == 2:
|
||||||
|
tags = '$|^'.join(tags)
|
||||||
|
else:
|
||||||
|
tags = ','.join(tags)
|
||||||
else:
|
else:
|
||||||
tags = ','.join(tags)
|
tags = unicode(tb.text()).strip()
|
||||||
|
|
||||||
if not tags or not (c or nfc):
|
if not tags or not (c or nfc):
|
||||||
continue
|
continue
|
||||||
if c not in self.colors:
|
if c not in self.colors:
|
||||||
@ -185,24 +241,33 @@ class TagWizard(QDialog):
|
|||||||
show=True, show_copy_button=False)
|
show=True, show_copy_button=False)
|
||||||
return False
|
return False
|
||||||
if re == 2:
|
if re == 2:
|
||||||
lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".\
|
if m:
|
||||||
format(tags, c, nfc))
|
lines.append(" in_list(field('{3}'), ',', '^{0}$', '{1}', '{2}')".\
|
||||||
|
format(tags, c, nfc, f))
|
||||||
|
else:
|
||||||
|
lines.append(" contains(field('{3}'), '{0}', '{1}', '{2}')".\
|
||||||
|
format(tags, c, nfc, f))
|
||||||
else:
|
else:
|
||||||
lines.append(" str_in_list(t, ',', '{0}', '{1}', '{2}')".\
|
if m:
|
||||||
format(tags, c, nfc))
|
lines.append(" str_in_list(field('{3}'), ',', '{0}', '{1}', '{2}')".\
|
||||||
|
format(tags, c, nfc, f))
|
||||||
|
else:
|
||||||
|
lines.append(" strcmp(field('{3}'), '{0}', '{2}', '{1}', '{2}')".\
|
||||||
|
format(tags, c, nfc, f))
|
||||||
res += ',\n'.join(lines)
|
res += ',\n'.join(lines)
|
||||||
res += ')\n'
|
res += ')\n'
|
||||||
self.template = res
|
self.template = res
|
||||||
res = ''
|
res = ''
|
||||||
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
|
||||||
self.nfcolorboxes, self.reboxes):
|
self.colboxes, self.nfcolorboxes, self.reboxes):
|
||||||
t = unicode(tb.text()).strip()
|
t = unicode(tb.text()).strip()
|
||||||
if t.endswith(','):
|
if t.endswith(','):
|
||||||
t = t[:-1]
|
t = t[:-1]
|
||||||
c = unicode(cb.currentText()).strip()
|
c = unicode(cb.currentText()).strip()
|
||||||
|
f = unicode(fb.currentText())
|
||||||
nfc = unicode(nfcb.currentText()).strip()
|
nfc = unicode(nfcb.currentText()).strip()
|
||||||
re = unicode(reb.checkState())
|
re = unicode(reb.checkState())
|
||||||
if t and c:
|
if f and t and c:
|
||||||
res += '#' + t + ':|:' + c + ':|:' + nfc + ':|:' + re + '\n'
|
res += '#' + t + ':|:' + c + ':|:' + f +':|:' + nfc + ':|:' + re + '\n'
|
||||||
self.template += res
|
self.template += res
|
||||||
self.accept()
|
self.accept()
|
||||||
|
@ -204,7 +204,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
choices.sort(key=sort_key)
|
choices.sort(key=sort_key)
|
||||||
choices.insert(0, '')
|
choices.insert(0, '')
|
||||||
self.column_color_count = db.column_color_count+1
|
self.column_color_count = db.column_color_count+1
|
||||||
tags = db.all_tags()
|
|
||||||
|
|
||||||
mi=None
|
mi=None
|
||||||
try:
|
try:
|
||||||
@ -217,7 +216,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
r('column_color_name_'+str(i), db.prefs, choices=choices)
|
r('column_color_name_'+str(i), db.prefs, choices=choices)
|
||||||
r('column_color_template_'+str(i), db.prefs)
|
r('column_color_template_'+str(i), db.prefs)
|
||||||
tpl = getattr(self, 'opt_column_color_template_'+str(i))
|
tpl = getattr(self, 'opt_column_color_template_'+str(i))
|
||||||
tpl.set_tags(tags)
|
tpl.set_db(db)
|
||||||
tpl.set_mi(mi)
|
tpl.set_mi(mi)
|
||||||
toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i))
|
toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i))
|
||||||
toolbutton.clicked.connect(tpl.tag_wizard)
|
toolbutton.clicked.connect(tpl.tag_wizard)
|
||||||
|
@ -331,9 +331,10 @@ class BuiltinInList(BuiltinFormatterFunction):
|
|||||||
|
|
||||||
def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
|
def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
|
||||||
l = [v.strip() for v in val.split(sep) if v.strip()]
|
l = [v.strip() for v in val.split(sep) if v.strip()]
|
||||||
for v in l:
|
if l:
|
||||||
if re.search(pat, v, flags=re.I):
|
for v in l:
|
||||||
return fv
|
if re.search(pat, v, flags=re.I):
|
||||||
|
return fv
|
||||||
return nfv
|
return nfv
|
||||||
|
|
||||||
class BuiltinStrInList(BuiltinFormatterFunction):
|
class BuiltinStrInList(BuiltinFormatterFunction):
|
||||||
@ -349,10 +350,11 @@ class BuiltinStrInList(BuiltinFormatterFunction):
|
|||||||
def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):
|
def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):
|
||||||
l = [v.strip() for v in val.split(sep) if v.strip()]
|
l = [v.strip() for v in val.split(sep) if v.strip()]
|
||||||
c = [v.strip() for v in str.split(sep) if v.strip()]
|
c = [v.strip() for v in str.split(sep) if v.strip()]
|
||||||
for v in l:
|
if l:
|
||||||
for t in c:
|
for v in l:
|
||||||
if strcmp(t, v) == 0:
|
for t in c:
|
||||||
return fv
|
if strcmp(t, v) == 0:
|
||||||
|
return fv
|
||||||
return nfv
|
return nfv
|
||||||
|
|
||||||
class BuiltinRe(BuiltinFormatterFunction):
|
class BuiltinRe(BuiltinFormatterFunction):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user