Allow specifying arbitrary colors for column coloring rules

This commit is contained in:
Kovid Goyal 2014-04-04 17:21:30 +05:30
parent a7d4fa16f0
commit fce3f56826
2 changed files with 66 additions and 24 deletions

View File

@ -10,7 +10,7 @@ __docformat__ = 'restructuredtext en'
import os, textwrap import os, textwrap
from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize, from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon, QLineEdit, QIntValidator, QDoubleValidator, QFrame, Qt, QIcon,
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton, QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem, QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
QApplication, QStandardItem, QStandardItemModel, QCheckBox) QApplication, QStandardItem, QStandardItemModel, QCheckBox)
@ -21,6 +21,7 @@ from calibre.utils.icu import sort_key
from calibre.gui2 import error_dialog, choose_files, pixmap_to_data from calibre.gui2 import error_dialog, choose_files, pixmap_to_data
from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.dialogs.template_dialog import TemplateDialog
from calibre.gui2.metadata.single_download import RichTextDelegate from calibre.gui2.metadata.single_download import RichTextDelegate
from calibre.gui2.widgets2 import ColorButton
from calibre.library.coloring import (Rule, conditionable_columns, from calibre.library.coloring import (Rule, conditionable_columns,
displayable_columns, rule_from_template, color_row_key) displayable_columns, rule_from_template, color_row_key)
from calibre.utils.localization import lang_map from calibre.utils.localization import lang_map
@ -33,7 +34,8 @@ icon_rule_kinds = [(_('icon with text'), 'icon'),
(_('composed icons w/text'), 'icon_composed'), (_('composed icons w/text'), 'icon_composed'),
(_('composed icons w/no text'), 'icon_only_composed'),] (_('composed icons w/no text'), 'icon_only_composed'),]
class ConditionEditor(QWidget): # {{{
class ConditionEditor(QWidget): # {{{
ACTION_MAP = { ACTION_MAP = {
'bool' : ( 'bool' : (
@ -86,7 +88,6 @@ class ConditionEditor(QWidget): # {{{
for x in ('float', 'rating'): for x in ('float', 'rating'):
ACTION_MAP[x] = ACTION_MAP['int'] ACTION_MAP[x] = ACTION_MAP['int']
def __init__(self, fm, parent=None): def __init__(self, fm, parent=None):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
self.fm = fm self.fm = fm
@ -108,8 +109,6 @@ class ConditionEditor(QWidget): # {{{
self.column_box = QComboBox(self) self.column_box = QComboBox(self)
l.addWidget(self.column_box, 0, 1) l.addWidget(self.column_box, 0, 1)
self.l2 = l2 = QLabel(two) self.l2 = l2 = QLabel(two)
l.addWidget(l2, 0, 2) l.addWidget(l2, 0, 2)
@ -277,7 +276,7 @@ class ConditionEditor(QWidget): # {{{
self.value_box.setEnabled(False) self.value_box.setEnabled(False)
# }}} # }}}
class RuleEditor(QDialog): # {{{ class RuleEditor(QDialog): # {{{
def __init__(self, fm, pref_name, parent=None): def __init__(self, fm, pref_name, parent=None):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
@ -329,7 +328,7 @@ class RuleEditor(QDialog): # {{{
l.addWidget(l4, 2, 4) l.addWidget(l4, 2, 4)
if self.rule_kind == 'color': if self.rule_kind == 'color':
self.color_box = QComboBox(self) self.color_box = ColorButton(parent=self)
self.color_label = QLabel('Sample text Sample text') self.color_label = QLabel('Sample text Sample text')
self.color_label.setTextFormat(Qt.RichText) self.color_label.setTextFormat(Qt.RichText)
l.addWidget(self.color_box, 2, 5) l.addWidget(self.color_box, 2, 5)
@ -393,7 +392,7 @@ class RuleEditor(QDialog): # {{{
self.conditions = [] self.conditions = []
if self.rule_kind == 'color': if self.rule_kind == 'color':
for b in (self.column_box, self.color_box): for b in (self.column_box, ):
b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon) b.setSizeAdjustPolicy(b.AdjustToMinimumContentsLengthWithIcon)
b.setMinimumContentsLength(15) b.setMinimumContentsLength(15)
@ -407,10 +406,9 @@ class RuleEditor(QDialog): # {{{
self.column_box.setCurrentIndex(0) self.column_box.setCurrentIndex(0)
if self.rule_kind == 'color': if self.rule_kind == 'color':
self.color_box.addItems(QColor.colorNames()) self.color_box.color = '#000'
self.color_box.setCurrentIndex(0)
self.update_color_label() self.update_color_label()
self.color_box.currentIndexChanged.connect(self.update_color_label) self.color_box.color_changed.connect(self.update_color_label)
else: else:
self.rule_icon_files = [] self.rule_icon_files = []
self.filename_button.clicked.connect(self.filename_button_clicked) self.filename_button.clicked.connect(self.filename_button_clicked)
@ -436,10 +434,10 @@ class RuleEditor(QDialog): # {{{
for i,filename in enumerate(self.icon_file_names): for i,filename in enumerate(self.icon_file_names):
item = QStandardItem(filename) item = QStandardItem(filename)
if doing_multiple: if doing_multiple:
item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled); item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
item.setData(Qt.Unchecked, Qt.CheckStateRole) item.setData(Qt.Unchecked, Qt.CheckStateRole)
else: else:
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable); item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable)
icon = QIcon(os.path.join(config_dir, 'cc_icons', filename)) icon = QIcon(os.path.join(config_dir, 'cc_icons', filename))
item.setIcon(icon) item.setIcon(icon)
model.appendRow(item) model.appendRow(item)
@ -448,7 +446,7 @@ class RuleEditor(QDialog): # {{{
pal = QApplication.palette() pal = QApplication.palette()
bg1 = unicode(pal.color(pal.Base).name()) bg1 = unicode(pal.color(pal.Base).name())
bg2 = unicode(pal.color(pal.AlternateBase).name()) bg2 = unicode(pal.color(pal.AlternateBase).name())
c = unicode(self.color_box.currentText()) c = self.color_box.color
self.color_label.setText(''' self.color_label.setText('''
<span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span> <span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
<span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span> <span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
@ -528,9 +526,7 @@ class RuleEditor(QDialog): # {{{
def apply_rule(self, kind, col, rule): def apply_rule(self, kind, col, rule):
if kind == 'color': if kind == 'color':
if rule.color: if rule.color:
idx = self.color_box.findText(rule.color) self.color_box.color = rule.color
if idx >= 0:
self.color_box.setCurrentIndex(idx)
else: else:
for i,tup in enumerate(icon_rule_kinds): for i,tup in enumerate(icon_rule_kinds):
if kind == tup[1]: if kind == tup[1]:
@ -595,7 +591,7 @@ class RuleEditor(QDialog): # {{{
if self.rule_kind != 'color': if self.rule_kind != 'color':
r.color = self.get_filenames_from_box() r.color = self.get_filenames_from_box()
else: else:
r.color = unicode(self.color_box.currentText()) r.color = self.color_box.color
idx = self.column_box.currentIndex() idx = self.column_box.currentIndex()
col = unicode(self.column_box.itemData(idx).toString()) col = unicode(self.column_box.itemData(idx).toString())
for c in self.conditions: for c in self.conditions:
@ -611,7 +607,7 @@ class RuleEditor(QDialog): # {{{
return kind, col, r return kind, col, r
# }}} # }}}
class RulesModel(QAbstractListModel): # {{{ class RulesModel(QAbstractListModel): # {{{
def __init__(self, prefs, fm, pref_name, parent=None): def __init__(self, prefs, fm, pref_name, parent=None):
QAbstractListModel.__init__(self, parent) QAbstractListModel.__init__(self, parent)
@ -623,7 +619,8 @@ class RulesModel(QAbstractListModel): # {{{
rules = list(prefs[pref_name]) rules = list(prefs[pref_name])
self.rules = [] self.rules = []
for col, template in rules: for col, template in rules:
if col not in self.fm and col != color_row_key: continue if col not in self.fm and col != color_row_key:
continue
try: try:
rule = rule_from_template(self.fm, template) rule = rule_from_template(self.fm, template)
except: except:
@ -634,7 +631,8 @@ class RulesModel(QAbstractListModel): # {{{
rules = list(prefs[pref_name]) rules = list(prefs[pref_name])
self.rules = [] self.rules = []
for kind, col, template in rules: for kind, col, template in rules:
if col not in self.fm and col != color_row_key: continue if col not in self.fm and col != color_row_key:
continue
try: try:
rule = rule_from_template(self.fm, template) rule = rule_from_template(self.fm, template)
except: except:
@ -764,7 +762,7 @@ class RulesModel(QAbstractListModel): # {{{
# }}} # }}}
class EditRules(QWidget): # {{{ class EditRules(QWidget): # {{{
changed = pyqtSignal() changed = pyqtSignal()
@ -881,7 +879,7 @@ class EditRules(QWidget): # {{{
icon_rule_kind=kind) icon_rule_kind=kind)
if d.exec_() == d.Accepted: if d.exec_() == d.Accepted:
if len(d.rule) == 2: # Convert template dialog rules to a triple if len(d.rule) == 2: # Convert template dialog rules to a triple
d.rule = ('color', d.rule[0], d.rule[1]) d.rule = ('color', d.rule[0], d.rule[1])
kind, col, r = d.rule kind, col, r = d.rule
if kind and r is not None and col: if kind and r is not None and col:
@ -945,7 +943,7 @@ if __name__ == '__main__':
d.add_blank_condition() d.add_blank_condition()
d.exec_() d.exec_()
col, r = d.rule kind, col, r = d.rule
print ('Column to be colored:', col) print ('Column to be colored:', col)
print ('Template:') print ('Template:')

View File

@ -6,6 +6,8 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
from PyQt4.Qt import QPushButton, QPixmap, QIcon, QColor, Qt, QColorDialog, pyqtSignal
from calibre.gui2.complete2 import LineEdit from calibre.gui2.complete2 import LineEdit
from calibre.gui2.widgets import history from calibre.gui2.widgets import history
@ -46,3 +48,45 @@ class HistoryLineEdit2(LineEdit):
history.set(self.store_name, self.history) history.set(self.store_name, self.history)
self.update_items_cache(self.history) self.update_items_cache(self.history)
class ColorButton(QPushButton):
color_changed = pyqtSignal(object)
def __init__(self, initial_color=None, parent=None, choose_text=None):
QPushButton.__init__(self, parent)
self._color = None
self.choose_text = choose_text or _('Choose &color')
self.color = initial_color
self.clicked.connect(self.choose_color)
@dynamic_property
def color(self):
def fget(self):
return self._color
def fset(self, val):
val = unicode(val or '')
col = QColor(val)
orig = self._color
if col.isValid():
self._color = val
self.setText(val)
p = QPixmap(self.iconSize())
p.fill(col)
self.setIcon(QIcon(p))
else:
self._color = None
self.setText(self.choose_text)
self.setIcon(QIcon())
if orig != col:
self.color_changed.emit(self._color)
return property(fget=fget, fset=fset)
def choose_color(self):
col = QColorDialog.getColor(QColor(self._color or Qt.white), self, _('Choose a color'))
if col.isValid():
r, g, b, a = col.getRgb()
if a != 255:
self.color = '#%02x%02x%02x%02x' % col.getRgb()
else:
self.color = '#%02x%02x%02x' % (r, g, b)