mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow an icon rule to put multiple icons on a column
This commit is contained in:
parent
b78885e01f
commit
089d6c172f
@ -9,7 +9,7 @@ import functools, re, os, traceback, errno, time
|
||||
from collections import defaultdict, namedtuple
|
||||
|
||||
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
|
||||
QModelIndex, QVariant, QDateTime, QColor, QPixmap)
|
||||
QModelIndex, QVariant, QDateTime, QColor, QPixmap, QPainter)
|
||||
|
||||
from calibre.gui2 import NONE, error_dialog
|
||||
from calibre.utils.search_query_parser import ParseException
|
||||
@ -76,9 +76,10 @@ class ColumnColor(object): # {{{
|
||||
|
||||
class ColumnIcon(object): # {{{
|
||||
|
||||
def __init__(self, formatter):
|
||||
def __init__(self, formatter, model):
|
||||
self.mi = None
|
||||
self.formatter = formatter
|
||||
self.model = model
|
||||
|
||||
def __call__(self, id_, key, fmt, kind, db, icon_cache, icon_bitmap_cache):
|
||||
dex = key+kind
|
||||
@ -88,26 +89,44 @@ class ColumnIcon(object): # {{{
|
||||
try:
|
||||
if self.mi is None:
|
||||
self.mi = db.get_metadata(id_, index_is_id=True)
|
||||
icon = self.formatter.safe_format(fmt, self.mi, '', self.mi)
|
||||
if icon:
|
||||
if icon in icon_bitmap_cache:
|
||||
icon_bitmap = icon_bitmap_cache[icon]
|
||||
icons = self.formatter.safe_format(fmt, self.mi, '', self.mi)
|
||||
if icons:
|
||||
if icons in icon_bitmap_cache:
|
||||
icon_bitmap = icon_bitmap_cache[icons]
|
||||
icon_cache[id_][dex] = icon_bitmap
|
||||
return icon_bitmap
|
||||
d = os.path.join(config_dir, 'cc_icons', icon)
|
||||
if (os.path.exists(d)):
|
||||
icon_bitmap = QPixmap(d)
|
||||
h = icon_bitmap.height()
|
||||
w = icon_bitmap.width()
|
||||
# If the image is landscape and width is more than 50%
|
||||
# large than height, use the pixmap. This tells Qt to display
|
||||
# the image full width. It might be clipped to row height.
|
||||
if w < (3 * h)/2:
|
||||
icon_bitmap = QIcon(icon_bitmap)
|
||||
icon_cache[id_][dex] = icon_bitmap
|
||||
icon_bitmap_cache[icon] = icon_bitmap
|
||||
self.mi = None
|
||||
return icon_bitmap
|
||||
|
||||
icon_list = [ic.strip() for ic in icons.split(':')]
|
||||
icon_bitmaps = []
|
||||
total_width = 0
|
||||
for icon in icon_list:
|
||||
d = os.path.join(config_dir, 'cc_icons', icon)
|
||||
if (os.path.exists(d)):
|
||||
bm = QPixmap(d)
|
||||
icon_bitmaps.append(bm)
|
||||
total_width += bm.width()
|
||||
if len(icon_bitmaps) > 1:
|
||||
result = QPixmap(len(icon_list)*128, 128)
|
||||
result.fill()
|
||||
painter = QPainter(result)
|
||||
x = 0
|
||||
for bm in icon_bitmaps:
|
||||
painter.drawPixmap(x, 0, bm)
|
||||
x += bm.width()
|
||||
painter.end()
|
||||
else:
|
||||
result = icon_bitmaps[0]
|
||||
|
||||
# If the image height is less than the row height, leave it alone
|
||||
# The -2 allows for a pixel above and below. Also ensure that
|
||||
# it is always a bit positive
|
||||
rh = min(2, self.model.row_height - 2)
|
||||
if result.height() > rh:
|
||||
result = result.scaledToHeight(rh, mode=Qt.SmoothTransformation)
|
||||
icon_cache[id_][dex] = result
|
||||
icon_bitmap_cache[icons] = result
|
||||
self.mi = None
|
||||
return result
|
||||
except:
|
||||
pass
|
||||
# }}}
|
||||
@ -145,7 +164,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
self.colors = frozenset([unicode(c) for c in QColor.colorNames()])
|
||||
self._clear_caches()
|
||||
self.column_color = ColumnColor(self.formatter, self.colors)
|
||||
self.column_icon = ColumnIcon(self.formatter)
|
||||
self.column_icon = ColumnIcon(self.formatter, self)
|
||||
|
||||
self.book_on_device = None
|
||||
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
||||
@ -168,6 +187,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
self.ids_to_highlight_set = set()
|
||||
self.current_highlighted_idx = None
|
||||
self.highlight_only = False
|
||||
self.row_height = 0
|
||||
self.read_config()
|
||||
|
||||
def _clear_caches(self):
|
||||
@ -176,6 +196,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
self.icon_bitmap_cache = {}
|
||||
self.color_row_fmt_cache = None
|
||||
|
||||
def set_row_height(self, height):
|
||||
self.row_height = height
|
||||
|
||||
def change_alignment(self, colname, alignment):
|
||||
if colname in self.column_map and alignment in ('left', 'right', 'center'):
|
||||
old = self.alignment_map.get(colname, 'left')
|
||||
|
@ -649,6 +649,7 @@ class BooksView(QTableView): # {{{
|
||||
self.resizeRowToContents(0)
|
||||
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0) +
|
||||
gprefs['extra_row_spacing'])
|
||||
self._model.set_row_height(self.rowHeight(0))
|
||||
self.row_sizing_done = True
|
||||
|
||||
def resize_column_to_fit(self, column):
|
||||
|
@ -13,7 +13,7 @@ from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
|
||||
QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon,
|
||||
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
|
||||
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
|
||||
QApplication)
|
||||
QApplication, QStandardItem, QStandardItemModel, QCheckBox)
|
||||
|
||||
from calibre import prepare_string_for_xml, sanitize_file_name_unicode
|
||||
from calibre.constants import config_dir
|
||||
@ -331,7 +331,6 @@ class RuleEditor(QDialog): # {{{
|
||||
l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
|
||||
else:
|
||||
self.filename_box = QComboBox()
|
||||
self.filename_box.setInsertPolicy(self.filename_box.InsertAlphabetically)
|
||||
d = os.path.join(config_dir, 'cc_icons')
|
||||
self.icon_file_names = []
|
||||
if os.path.exists(d):
|
||||
@ -341,9 +340,15 @@ class RuleEditor(QDialog): # {{{
|
||||
if icon_file.endswith('.png'):
|
||||
self.icon_file_names.append(icon_file)
|
||||
self.icon_file_names.sort(key=sort_key)
|
||||
self.update_filename_box()
|
||||
|
||||
l.addWidget(self.filename_box, 2, 5)
|
||||
vb = QVBoxLayout()
|
||||
self.multiple_icon_cb = QCheckBox(_('Choose more than one icon'))
|
||||
vb.addWidget(self.multiple_icon_cb)
|
||||
self.update_filename_box()
|
||||
self.multiple_icon_cb.clicked.connect(self.multiple_box_clicked)
|
||||
vb.addWidget(self.filename_box)
|
||||
l.addLayout(vb, 2, 5)
|
||||
|
||||
self.filename_button = QPushButton(QIcon(I('document_open.png')),
|
||||
_('&Add icon'))
|
||||
l.addWidget(self.filename_button, 2, 6)
|
||||
@ -401,18 +406,37 @@ class RuleEditor(QDialog): # {{{
|
||||
self.update_color_label()
|
||||
self.color_box.currentIndexChanged.connect(self.update_color_label)
|
||||
else:
|
||||
self.rule_icon_files = []
|
||||
self.filename_button.clicked.connect(self.filename_button_clicked)
|
||||
|
||||
self.resize(self.sizeHint())
|
||||
|
||||
def multiple_box_clicked(self):
|
||||
self.update_filename_box()
|
||||
self.update_icon_filenames_in_box()
|
||||
|
||||
def update_filename_box(self):
|
||||
self.filename_box.clear()
|
||||
doing_multiple = self.multiple_icon_cb.isChecked()
|
||||
|
||||
model = QStandardItemModel()
|
||||
self.filename_box.setModel(model)
|
||||
self.icon_file_names.sort(key=sort_key)
|
||||
self.filename_box.addItem('')
|
||||
self.filename_box.addItems(self.icon_file_names)
|
||||
if doing_multiple:
|
||||
item = QStandardItem(_('Open to see checkboxes'))
|
||||
else:
|
||||
item = QStandardItem('')
|
||||
model.appendRow(item)
|
||||
|
||||
for i,filename in enumerate(self.icon_file_names):
|
||||
item = QStandardItem(filename)
|
||||
if doing_multiple:
|
||||
item.setFlags(Qt.ItemIsUserCheckable | Qt.ItemIsEnabled);
|
||||
item.setData(Qt.Unchecked, Qt.CheckStateRole)
|
||||
else:
|
||||
item.setFlags(Qt.ItemIsEnabled | Qt.ItemIsSelectable);
|
||||
icon = QIcon(os.path.join(config_dir, 'cc_icons', filename))
|
||||
self.filename_box.setItemIcon(i+1, icon)
|
||||
item.setIcon(icon)
|
||||
model.appendRow(item)
|
||||
|
||||
def update_color_label(self):
|
||||
pal = QApplication.palette()
|
||||
@ -432,9 +456,9 @@ class RuleEditor(QDialog): # {{{
|
||||
all_files=False, select_only_single_file=True)
|
||||
if path:
|
||||
icon_path = path[0]
|
||||
icon_name = sanitize_file_name_unicode(
|
||||
icon_name = lower(sanitize_file_name_unicode(
|
||||
os.path.splitext(
|
||||
os.path.basename(icon_path))[0]+'.png')
|
||||
os.path.basename(icon_path))[0]+'.png'))
|
||||
if icon_name not in self.icon_file_names:
|
||||
self.icon_file_names.append(icon_name)
|
||||
self.update_filename_box()
|
||||
@ -449,13 +473,47 @@ class RuleEditor(QDialog): # {{{
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
self.filename_box.setCurrentIndex(self.filename_box.findText(icon_name))
|
||||
if self.multiple_icon_cb.isChecked():
|
||||
if icon_name not in self.rule_icon_files:
|
||||
self.rule_icon_files.append(icon_name)
|
||||
self.update_icon_filenames_in_box()
|
||||
else:
|
||||
self.filename_box.setCurrentIndex(self.filename_box.findText(icon_name))
|
||||
self.filename_box.adjustSize()
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
def get_filenames_from_box(self):
|
||||
if self.multiple_icon_cb.isChecked():
|
||||
model = self.filename_box.model()
|
||||
fnames = []
|
||||
for i in range(1, model.rowCount()):
|
||||
item = model.item(i, 0)
|
||||
if item.checkState() == Qt.Checked:
|
||||
fnames.append(lower(unicode(item.text())))
|
||||
fname = ' : '.join(fnames)
|
||||
else:
|
||||
fname = lower(unicode(self.filename_box.currentText()))
|
||||
return fname
|
||||
|
||||
def update_icon_filenames_in_box(self):
|
||||
if self.rule_icon_files:
|
||||
if not self.multiple_icon_cb.isChecked():
|
||||
idx = self.filename_box.findText(self.rule_icon_files[0])
|
||||
if idx >= 0:
|
||||
self.filename_box.setCurrentIndex(idx)
|
||||
else:
|
||||
self.filename_box.setCurrentIndex(0)
|
||||
else:
|
||||
model = self.filename_box.model()
|
||||
for icon in self.rule_icon_files:
|
||||
idx = self.filename_box.findText(icon)
|
||||
if idx >= 0:
|
||||
item = model.item(idx)
|
||||
item.setCheckState(Qt.Checked)
|
||||
|
||||
def add_blank_condition(self):
|
||||
c = ConditionEditor(self.fm, parent=self.conditions_widget)
|
||||
self.conditions.append(c)
|
||||
@ -469,12 +527,11 @@ class RuleEditor(QDialog): # {{{
|
||||
self.color_box.setCurrentIndex(idx)
|
||||
else:
|
||||
self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1)
|
||||
if rule.color:
|
||||
idx = self.filename_box.findText(rule.color)
|
||||
if idx >= 0:
|
||||
self.filename_box.setCurrentIndex(idx)
|
||||
else:
|
||||
self.filename_box.setCurrentIndex(0)
|
||||
self.rule_icon_files = [ic.strip() for ic in rule.color.split(':')]
|
||||
if len(self.rule_icon_files) > 1:
|
||||
self.multiple_icon_cb.setChecked(True)
|
||||
self.update_filename_box()
|
||||
self.update_icon_filenames_in_box()
|
||||
|
||||
for i in range(self.column_box.count()):
|
||||
c = unicode(self.column_box.itemData(i).toString())
|
||||
@ -492,10 +549,9 @@ class RuleEditor(QDialog): # {{{
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
def accept(self):
|
||||
if self.rule_kind != 'color':
|
||||
fname = lower(unicode(self.filename_box.currentText()))
|
||||
fname = self.get_filenames_from_box()
|
||||
if not fname:
|
||||
error_dialog(self, _('No icon selected'),
|
||||
_('You must choose an icon for this rule'), show=True)
|
||||
@ -528,7 +584,7 @@ class RuleEditor(QDialog): # {{{
|
||||
def rule(self):
|
||||
r = Rule(self.fm)
|
||||
if self.rule_kind != 'color':
|
||||
r.color = unicode(self.filename_box.currentText())
|
||||
r.color = self.get_filenames_from_box()
|
||||
else:
|
||||
r.color = unicode(self.color_box.currentText())
|
||||
idx = self.column_box.currentIndex()
|
||||
|
Loading…
x
Reference in New Issue
Block a user