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>' __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()

View File

@ -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)

View File

@ -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):