mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Tag wizard for column coloring
This commit is contained in:
commit
75daa16386
@ -5,8 +5,12 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
from PyQt4.Qt import QLineEdit
|
from PyQt4.Qt import (QLineEdit, QDialog, QGridLayout, QLabel,
|
||||||
|
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 MultiCompleteComboBox
|
||||||
|
from calibre.gui2 import error_dialog
|
||||||
|
|
||||||
class TemplateLineEditor(QLineEdit):
|
class TemplateLineEditor(QLineEdit):
|
||||||
|
|
||||||
@ -14,13 +18,22 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
Extend the context menu of a QLineEdit to include more actions.
|
Extend the context menu of a QLineEdit to include more actions.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
def __init__(self, parent):
|
||||||
|
QLineEdit.__init__(self, parent)
|
||||||
|
self.tags = None
|
||||||
|
|
||||||
|
def set_tags(self, tags):
|
||||||
|
self.tags = tags
|
||||||
|
|
||||||
def contextMenuEvent(self, event):
|
def contextMenuEvent(self, event):
|
||||||
menu = self.createStandardContextMenu()
|
menu = self.createStandardContextMenu()
|
||||||
menu.addSeparator()
|
menu.addSeparator()
|
||||||
|
|
||||||
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):
|
||||||
@ -29,4 +42,89 @@ class TemplateLineEditor(QLineEdit):
|
|||||||
if t.exec_():
|
if t.exec_():
|
||||||
self.setText(t.textbox.toPlainText())
|
self.setText(t.textbox.toPlainText())
|
||||||
|
|
||||||
|
def tag_wizard(self):
|
||||||
|
txt = unicode(self.text())
|
||||||
|
if txt and not txt.startswith('program:\n#tag wizard'):
|
||||||
|
error_dialog(self, _('Invalid text'),
|
||||||
|
_('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()))
|
||||||
|
if d.exec_():
|
||||||
|
self.setText(d.template)
|
||||||
|
|
||||||
|
class TagWizard(QDialog):
|
||||||
|
|
||||||
|
def __init__(self, parent, tags, txt):
|
||||||
|
QDialog.__init__(self, parent)
|
||||||
|
self.setWindowTitle(_('Tag Wizard'))
|
||||||
|
self.setWindowIcon(QIcon(I('wizard.png')))
|
||||||
|
|
||||||
|
self.tags = tags
|
||||||
|
l = QGridLayout()
|
||||||
|
self.setLayout(l)
|
||||||
|
l.addWidget(QLabel(_('Tag Value')), 0, 0, 1, 1)
|
||||||
|
l.addWidget(QLabel(_('Color')), 0, 1, 1, 1)
|
||||||
|
self.tagboxes = []
|
||||||
|
self.colorboxes = []
|
||||||
|
self.colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
|
self.colors.insert(0, '')
|
||||||
|
for i in range(0, 10):
|
||||||
|
tb = MultiCompleteComboBox(self)
|
||||||
|
tb.set_separator(', ')
|
||||||
|
tb.update_items_cache(self.tags)
|
||||||
|
self.tagboxes.append(tb)
|
||||||
|
l.addWidget(tb, i+1, 0, 1, 1)
|
||||||
|
cb = QComboBox(self)
|
||||||
|
cb.addItems(self.colors)
|
||||||
|
self.colorboxes.append(cb)
|
||||||
|
l.addWidget(cb, i+1, 1, 1, 1)
|
||||||
|
|
||||||
|
if txt:
|
||||||
|
lines = txt.split('\n')[3:]
|
||||||
|
i = 0
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('#'):
|
||||||
|
t,c = line[1:].split(':|:')
|
||||||
|
try:
|
||||||
|
self.colorboxes[i].setCurrentIndex(self.colorboxes[i].findText(c))
|
||||||
|
self.tagboxes[i].setText(t)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
bb = QDialogButtonBox(QDialogButtonBox.Ok|QDialogButtonBox.Cancel, parent=self)
|
||||||
|
l.addWidget(bb, 100, 1, 1, 1)
|
||||||
|
bb.accepted.connect(self.accepted)
|
||||||
|
bb.rejected.connect(self.reject)
|
||||||
|
self.template = ''
|
||||||
|
|
||||||
|
def accepted(self):
|
||||||
|
res = ("program:\n#tag wizard -- do not directly edit\n"
|
||||||
|
" t = field('tags');\n first_non_empty(\n")
|
||||||
|
lines = []
|
||||||
|
for tb, cb in zip(self.tagboxes, self.colorboxes):
|
||||||
|
tags = [t.strip() for t in unicode(tb.currentText()).split(',') if t.strip()]
|
||||||
|
c = unicode(cb.currentText()).strip()
|
||||||
|
if not tags or not c:
|
||||||
|
continue
|
||||||
|
if c not in self.colors:
|
||||||
|
error_dialog(self, _('Invalid color'),
|
||||||
|
_('The color {0} is not valid').format(c),
|
||||||
|
show=True, show_copy_button=False)
|
||||||
|
return False
|
||||||
|
for t in tags:
|
||||||
|
lines.append(" in_list(t, ',', '^{0}$', '{1}', '')".format(t, c))
|
||||||
|
res += ',\n'.join(lines)
|
||||||
|
res += ')\n'
|
||||||
|
self.template = res
|
||||||
|
res = ''
|
||||||
|
for tb, cb in zip(self.tagboxes, self.colorboxes):
|
||||||
|
t = unicode(tb.currentText()).strip()
|
||||||
|
if t.endswith(','):
|
||||||
|
t = t[:-1]
|
||||||
|
c = unicode(cb.currentText()).strip()
|
||||||
|
if t and c:
|
||||||
|
res += '#' + t + ':|:' + c + '\n'
|
||||||
|
self.template += res
|
||||||
|
self.accept()
|
||||||
|
@ -98,6 +98,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.current_highlighted_idx = None
|
self.current_highlighted_idx = None
|
||||||
self.highlight_only = False
|
self.highlight_only = False
|
||||||
self.column_color_map = {}
|
self.column_color_map = {}
|
||||||
|
self.colors = [unicode(c) for c in QColor.colorNames()]
|
||||||
self.read_config()
|
self.read_config()
|
||||||
|
|
||||||
def change_alignment(self, colname, alignment):
|
def change_alignment(self, colname, alignment):
|
||||||
@ -714,9 +715,11 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
mi = self.db.get_metadata(self.id(index), index_is_id=True)
|
mi = self.db.get_metadata(self.id(index), index_is_id=True)
|
||||||
fmt = self.column_color_map[key]
|
fmt = self.column_color_map[key]
|
||||||
try:
|
try:
|
||||||
color = QColor(composite_formatter.safe_format(fmt, mi, '', mi))
|
color = composite_formatter.safe_format(fmt, mi, '', mi)
|
||||||
if color.isValid():
|
if color in self.colors:
|
||||||
return QVariant(color)
|
color = QColor(color)
|
||||||
|
if color.isValid():
|
||||||
|
return QVariant(color)
|
||||||
except:
|
except:
|
||||||
return NONE
|
return NONE
|
||||||
elif self.is_custom_column(key) and \
|
elif self.is_custom_column(key) and \
|
||||||
|
@ -167,6 +167,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
'<a href="http://calibre-ebook.com/user_manual/template_lang.html">'
|
'<a href="http://calibre-ebook.com/user_manual/template_lang.html">'
|
||||||
'tutorial</a> on using templates.') +
|
'tutorial</a> on using templates.') +
|
||||||
'</p><p>' +
|
'</p><p>' +
|
||||||
|
_('If you want to color a field based on tags, then right-click '
|
||||||
|
'in an empty template line and choose tags wizard. '
|
||||||
|
'It will build a template for you. You can later edit that '
|
||||||
|
'template with the same wizard. If you edit it by hand, the '
|
||||||
|
'wizard might not work or might restore old values.') +
|
||||||
|
'</p><p>' +
|
||||||
_('The template must evaluate to one of the color names shown '
|
_('The template must evaluate to one of the color names shown '
|
||||||
'below. You can use any legal template expression. '
|
'below. You can use any legal template expression. '
|
||||||
'For example, you can set the title to always display in '
|
'For example, you can set the title to always display in '
|
||||||
@ -198,9 +204,12 @@ 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()
|
||||||
for i in range(1, self.column_color_count):
|
for i in range(1, self.column_color_count):
|
||||||
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)
|
||||||
|
temp = getattr(self, 'opt_column_color_template_'+str(i))
|
||||||
|
temp.set_tags(tags)
|
||||||
all_colors = [unicode(s) for s in list(QColor.colorNames())]
|
all_colors = [unicode(s) for s in list(QColor.colorNames())]
|
||||||
self.colors_box.setText(', '.join(all_colors))
|
self.colors_box.setText(', '.join(all_colors))
|
||||||
|
|
||||||
@ -273,9 +282,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
def commit(self, *args):
|
def commit(self, *args):
|
||||||
for i in range(1, self.column_color_count):
|
for i in range(1, self.column_color_count):
|
||||||
col = getattr(self, 'opt_column_color_name_'+str(i))
|
col = getattr(self, 'opt_column_color_name_'+str(i))
|
||||||
if not col.currentText():
|
tpl = getattr(self, 'opt_column_color_template_'+str(i))
|
||||||
temp = getattr(self, 'opt_column_color_template_'+str(i))
|
if not col.currentIndex() or not unicode(tpl.text()).strip():
|
||||||
temp.setText('')
|
col.setCurrentIndex(0)
|
||||||
|
tpl.setText('')
|
||||||
rr = ConfigWidgetBase.commit(self, *args)
|
rr = ConfigWidgetBase.commit(self, *args)
|
||||||
if self.current_font != self.initial_font:
|
if self.current_font != self.initial_font:
|
||||||
gprefs['font'] = (self.current_font[:4] if self.current_font else
|
gprefs['font'] = (self.current_font[:4] if self.current_font else
|
||||||
|
@ -419,14 +419,14 @@ then the tags will be displayed each on their own line.</string>
|
|||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Column name</string>
|
<string>Column to color</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="1">
|
||||||
<widget class="QLabel" name="label">
|
<widget class="QLabel" name="label">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Selection template</string>
|
<string>Color selection template</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
@ -215,7 +215,7 @@ class TemplateFormatter(string.Formatter):
|
|||||||
(r'\w+', lambda x,t: (2, t)),
|
(r'\w+', lambda x,t: (2, t)),
|
||||||
(r'".*?((?<!\\)")', lambda x,t: (3, t[1:-1])),
|
(r'".*?((?<!\\)")', lambda x,t: (3, t[1:-1])),
|
||||||
(r'\'.*?((?<!\\)\')', lambda x,t: (3, t[1:-1])),
|
(r'\'.*?((?<!\\)\')', lambda x,t: (3, t[1:-1])),
|
||||||
(r'\n#.*?(?=\n)', None),
|
(r'\n#.*?(?:(?=\n)|$)', None),
|
||||||
(r'\s', None)
|
(r'\s', None)
|
||||||
], flags=re.DOTALL)
|
], flags=re.DOTALL)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user