Add field box to color wizard

This commit is contained in:
Charles Haley 2011-05-29 20:12:31 +01:00
parent f25c423261
commit 19261f15ee
3 changed files with 127 additions and 61 deletions

View File

@ -5,12 +5,16 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from functools import partial
from collections import defaultdict
from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel, QCheckBox,
QDialogButtonBox, QColor, QComboBox, QIcon)
from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.complete import MultiCompleteLineEdit
from calibre.gui2 import error_dialog
from calibre.utils.icu import sort_key
class TemplateLineEditor(QLineEdit):
@ -26,8 +30,8 @@ class TemplateLineEditor(QLineEdit):
def set_mi(self, mi):
self.mi = mi
def set_tags(self, tags):
self.tags = tags
def set_db(self, db):
self.db = db
def contextMenuEvent(self, event):
menu = self.createStandardContextMenu()
@ -35,9 +39,6 @@ class TemplateLineEditor(QLineEdit):
action_open_editor = menu.addAction(_('Open Template 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())
def open_editor(self):
@ -53,84 +54,117 @@ class TemplateLineEditor(QLineEdit):
_('The text in the box was not generated by this wizard'),
show=True, show_copy_button=False)
return
d = TagWizard(self, self.tags, unicode(self.text()))
d = TagWizard(self, self.db, unicode(self.text()))
if d.exec_():
self.setText(d.template)
class TagWizard(QDialog):
def __init__(self, parent, tags, txt):
def __init__(self, parent, db, txt):
QDialog.__init__(self, parent)
self.setWindowTitle(_('Tag Wizard'))
self.setWindowTitle(_('Coloring Wizard'))
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()
self.setLayout(l)
l.setColumnStretch(0, 1)
l.setColumnMinimumWidth(0, 300)
h = QLabel(_('Tags (see the popup help for more information)'))
l.setColumnStretch(1, 10)
l.setColumnMinimumWidth(1, 300)
h = QLabel(_('Column'))
l.addWidget(h, 0, 0, 1, 1)
h = QLabel(_('Values (see the popup help for more information)'))
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>'
'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 '
'puts anchors (^ and $) around the expression, so you '
'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>' +
_('<li><code><b>.*</b></code> matches any tag. No empty tags are '
'checked, so you don\'t need to worry about empty strings</li>'
'<li><code><b>A.*</b></code> matches any tag beginning with A</li>'
'<li><code><b>.*mystery.*</b></code> matches any tag containing '
_('<li><code><b>.*</b></code> matches anything in the column. No '
'empty values are checked, so you don\'t need to worry about '
'empty strings</li>'
'<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>')
l.addWidget(h , 0, 0, 1, 1)
l.addWidget(h , 0, 1, 1, 1)
c = QLabel(_('is RE'))
c.setToolTip('<p>' +
_('Check this box if the tag box contains regular expressions') + '</p>')
l.addWidget(c, 0, 1, 1, 1)
_('Check this box if the values box contains regular expressions') + '</p>')
l.addWidget(c, 0, 2, 1, 1)
c = QLabel(_('Color if tag found'))
c = QLabel(_('Color if value found'))
c.setToolTip('<p>' +
_('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 '
'line in this wizard. If both boxes are filled in, the rest of '
'the lines in this wizard will be ignored.') + '</p>')
l.addWidget(c, 0, 2, 1, 1)
c = QLabel(_('Color if tag not found'))
l.addWidget(c, 0, 3, 1, 1)
c = QLabel(_('Color if value not found'))
c.setToolTip('<p>' +
_('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>')
l.addWidget(c, 0, 3, 1, 1)
l.addWidget(c, 0, 4, 1, 1)
self.tagboxes = []
self.colorboxes = []
self.nfcolorboxes = []
self.reboxes = []
self.colboxes = []
self.colors = [unicode(s) for s in list(QColor.colorNames())]
self.colors.insert(0, '')
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.set_separator(', ')
tb.update_items_cache(self.tags)
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)
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)
w = QComboBox(self)
w.addItems(self.colors)
self.nfcolorboxes.append(w)
self.colorboxes.append(w)
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:
lines = txt.split('\n')[3:]
i = 0
@ -141,37 +175,59 @@ class TagWizard(QDialog):
t, c = vals
nc = ''
re = False
f = 'tags'
else:
t,c,nc,re = vals
t,c,f,nc,re = vals
try:
self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
self.nfcolorboxes[i].setCurrentIndex(self.nfcolorboxes[i].findText(nc))
self.tagboxes[i].setText(t)
self.reboxes[i].setChecked(re == '2')
self.colboxes[i].setCurrentIndex(self.colboxes[i].findText(f))
except:
pass
i += 1
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.rejected.connect(self.reject)
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):
res = ("program:\n#tag wizard -- do not directly edit\n"
" t = field('tags');\n first_non_empty(\n")
" first_non_empty(\n")
lines = []
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
self.nfcolorboxes, self.reboxes):
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
self.colboxes, self.nfcolorboxes, self.reboxes):
f = unicode(fb.currentText())
if not f:
continue
m = self.completion_values[f]['m']
c = unicode(cb.currentText()).strip()
nfc = unicode(nfcb.currentText()).strip()
re = reb.checkState()
if m:
tags = [t.strip() for t in unicode(tb.text()).split(',') if t.strip()]
if re == 2:
tags = '$|^'.join(tags)
else:
tags = ','.join(tags)
else:
tags = unicode(tb.text()).strip()
if not tags or not (c or nfc):
continue
if c not in self.colors:
@ -185,24 +241,33 @@ class TagWizard(QDialog):
show=True, show_copy_button=False)
return False
if re == 2:
lines.append(" in_list(t, ',', '^{0}$', '{1}', '{2}')".\
format(tags, c, nfc))
if m:
lines.append(" in_list(field('{3}'), ',', '^{0}$', '{1}', '{2}')".\
format(tags, c, nfc, f))
else:
lines.append(" str_in_list(t, ',', '{0}', '{1}', '{2}')".\
format(tags, c, nfc))
lines.append(" contains(field('{3}'), '{0}', '{1}', '{2}')".\
format(tags, c, nfc, f))
else:
if m:
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'
self.template = res
res = ''
for tb, cb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
self.nfcolorboxes, self.reboxes):
for tb, cb, fb, nfcb, reb in zip(self.tagboxes, self.colorboxes,
self.colboxes, self.nfcolorboxes, self.reboxes):
t = unicode(tb.text()).strip()
if t.endswith(','):
t = t[:-1]
c = unicode(cb.currentText()).strip()
f = unicode(fb.currentText())
nfc = unicode(nfcb.currentText()).strip()
re = unicode(reb.checkState())
if t and c:
res += '#' + t + ':|:' + c + ':|:' + nfc + ':|:' + re + '\n'
if f and t and c:
res += '#' + t + ':|:' + c + ':|:' + f +':|:' + nfc + ':|:' + re + '\n'
self.template += res
self.accept()

View File

@ -204,7 +204,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
choices.sort(key=sort_key)
choices.insert(0, '')
self.column_color_count = db.column_color_count+1
tags = db.all_tags()
mi=None
try:
@ -217,7 +216,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
r('column_color_name_'+str(i), db.prefs, choices=choices)
r('column_color_template_'+str(i), db.prefs)
tpl = getattr(self, 'opt_column_color_template_'+str(i))
tpl.set_tags(tags)
tpl.set_db(db)
tpl.set_mi(mi)
toolbutton = getattr(self, 'opt_column_color_wizard_'+str(i))
toolbutton.clicked.connect(tpl.tag_wizard)

View File

@ -331,6 +331,7 @@ class BuiltinInList(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
l = [v.strip() for v in val.split(sep) if v.strip()]
if l:
for v in l:
if re.search(pat, v, flags=re.I):
return fv
@ -349,6 +350,7 @@ class BuiltinStrInList(BuiltinFormatterFunction):
def evaluate(self, formatter, kwargs, mi, locals, val, sep, str, fv, nfv):
l = [v.strip() for v in val.split(sep) if v.strip()]
c = [v.strip() for v in str.split(sep) if v.strip()]
if l:
for v in l:
for t in c:
if strcmp(t, v) == 0: