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 collections import defaultdict, namedtuple
|
||||||
|
|
||||||
from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
|
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.gui2 import NONE, error_dialog
|
||||||
from calibre.utils.search_query_parser import ParseException
|
from calibre.utils.search_query_parser import ParseException
|
||||||
@ -76,9 +76,10 @@ class ColumnColor(object): # {{{
|
|||||||
|
|
||||||
class ColumnIcon(object): # {{{
|
class ColumnIcon(object): # {{{
|
||||||
|
|
||||||
def __init__(self, formatter):
|
def __init__(self, formatter, model):
|
||||||
self.mi = None
|
self.mi = None
|
||||||
self.formatter = formatter
|
self.formatter = formatter
|
||||||
|
self.model = model
|
||||||
|
|
||||||
def __call__(self, id_, key, fmt, kind, db, icon_cache, icon_bitmap_cache):
|
def __call__(self, id_, key, fmt, kind, db, icon_cache, icon_bitmap_cache):
|
||||||
dex = key+kind
|
dex = key+kind
|
||||||
@ -88,26 +89,44 @@ class ColumnIcon(object): # {{{
|
|||||||
try:
|
try:
|
||||||
if self.mi is None:
|
if self.mi is None:
|
||||||
self.mi = db.get_metadata(id_, index_is_id=True)
|
self.mi = db.get_metadata(id_, index_is_id=True)
|
||||||
icon = self.formatter.safe_format(fmt, self.mi, '', self.mi)
|
icons = self.formatter.safe_format(fmt, self.mi, '', self.mi)
|
||||||
if icon:
|
if icons:
|
||||||
if icon in icon_bitmap_cache:
|
if icons in icon_bitmap_cache:
|
||||||
icon_bitmap = icon_bitmap_cache[icon]
|
icon_bitmap = icon_bitmap_cache[icons]
|
||||||
icon_cache[id_][dex] = icon_bitmap
|
icon_cache[id_][dex] = icon_bitmap
|
||||||
return icon_bitmap
|
return icon_bitmap
|
||||||
d = os.path.join(config_dir, 'cc_icons', icon)
|
|
||||||
if (os.path.exists(d)):
|
icon_list = [ic.strip() for ic in icons.split(':')]
|
||||||
icon_bitmap = QPixmap(d)
|
icon_bitmaps = []
|
||||||
h = icon_bitmap.height()
|
total_width = 0
|
||||||
w = icon_bitmap.width()
|
for icon in icon_list:
|
||||||
# If the image is landscape and width is more than 50%
|
d = os.path.join(config_dir, 'cc_icons', icon)
|
||||||
# large than height, use the pixmap. This tells Qt to display
|
if (os.path.exists(d)):
|
||||||
# the image full width. It might be clipped to row height.
|
bm = QPixmap(d)
|
||||||
if w < (3 * h)/2:
|
icon_bitmaps.append(bm)
|
||||||
icon_bitmap = QIcon(icon_bitmap)
|
total_width += bm.width()
|
||||||
icon_cache[id_][dex] = icon_bitmap
|
if len(icon_bitmaps) > 1:
|
||||||
icon_bitmap_cache[icon] = icon_bitmap
|
result = QPixmap(len(icon_list)*128, 128)
|
||||||
self.mi = None
|
result.fill()
|
||||||
return icon_bitmap
|
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:
|
except:
|
||||||
pass
|
pass
|
||||||
# }}}
|
# }}}
|
||||||
@ -145,7 +164,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.colors = frozenset([unicode(c) for c in QColor.colorNames()])
|
self.colors = frozenset([unicode(c) for c in QColor.colorNames()])
|
||||||
self._clear_caches()
|
self._clear_caches()
|
||||||
self.column_color = ColumnColor(self.formatter, self.colors)
|
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.book_on_device = None
|
||||||
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
self.editable_cols = ['title', 'authors', 'rating', 'publisher',
|
||||||
@ -168,6 +187,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.ids_to_highlight_set = set()
|
self.ids_to_highlight_set = set()
|
||||||
self.current_highlighted_idx = None
|
self.current_highlighted_idx = None
|
||||||
self.highlight_only = False
|
self.highlight_only = False
|
||||||
|
self.row_height = 0
|
||||||
self.read_config()
|
self.read_config()
|
||||||
|
|
||||||
def _clear_caches(self):
|
def _clear_caches(self):
|
||||||
@ -176,6 +196,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
self.icon_bitmap_cache = {}
|
self.icon_bitmap_cache = {}
|
||||||
self.color_row_fmt_cache = None
|
self.color_row_fmt_cache = None
|
||||||
|
|
||||||
|
def set_row_height(self, height):
|
||||||
|
self.row_height = height
|
||||||
|
|
||||||
def change_alignment(self, colname, alignment):
|
def change_alignment(self, colname, alignment):
|
||||||
if colname in self.column_map and alignment in ('left', 'right', 'center'):
|
if colname in self.column_map and alignment in ('left', 'right', 'center'):
|
||||||
old = self.alignment_map.get(colname, 'left')
|
old = self.alignment_map.get(colname, 'left')
|
||||||
|
@ -649,6 +649,7 @@ class BooksView(QTableView): # {{{
|
|||||||
self.resizeRowToContents(0)
|
self.resizeRowToContents(0)
|
||||||
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0) +
|
self.verticalHeader().setDefaultSectionSize(self.rowHeight(0) +
|
||||||
gprefs['extra_row_spacing'])
|
gprefs['extra_row_spacing'])
|
||||||
|
self._model.set_row_height(self.rowHeight(0))
|
||||||
self.row_sizing_done = True
|
self.row_sizing_done = True
|
||||||
|
|
||||||
def resize_column_to_fit(self, column):
|
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,
|
QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon,
|
||||||
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
|
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
|
||||||
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
|
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
|
||||||
QApplication)
|
QApplication, QStandardItem, QStandardItemModel, QCheckBox)
|
||||||
|
|
||||||
from calibre import prepare_string_for_xml, sanitize_file_name_unicode
|
from calibre import prepare_string_for_xml, sanitize_file_name_unicode
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
@ -331,7 +331,6 @@ class RuleEditor(QDialog): # {{{
|
|||||||
l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
|
l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 7)
|
||||||
else:
|
else:
|
||||||
self.filename_box = QComboBox()
|
self.filename_box = QComboBox()
|
||||||
self.filename_box.setInsertPolicy(self.filename_box.InsertAlphabetically)
|
|
||||||
d = os.path.join(config_dir, 'cc_icons')
|
d = os.path.join(config_dir, 'cc_icons')
|
||||||
self.icon_file_names = []
|
self.icon_file_names = []
|
||||||
if os.path.exists(d):
|
if os.path.exists(d):
|
||||||
@ -341,9 +340,15 @@ class RuleEditor(QDialog): # {{{
|
|||||||
if icon_file.endswith('.png'):
|
if icon_file.endswith('.png'):
|
||||||
self.icon_file_names.append(icon_file)
|
self.icon_file_names.append(icon_file)
|
||||||
self.icon_file_names.sort(key=sort_key)
|
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')),
|
self.filename_button = QPushButton(QIcon(I('document_open.png')),
|
||||||
_('&Add icon'))
|
_('&Add icon'))
|
||||||
l.addWidget(self.filename_button, 2, 6)
|
l.addWidget(self.filename_button, 2, 6)
|
||||||
@ -401,18 +406,37 @@ class RuleEditor(QDialog): # {{{
|
|||||||
self.update_color_label()
|
self.update_color_label()
|
||||||
self.color_box.currentIndexChanged.connect(self.update_color_label)
|
self.color_box.currentIndexChanged.connect(self.update_color_label)
|
||||||
else:
|
else:
|
||||||
|
self.rule_icon_files = []
|
||||||
self.filename_button.clicked.connect(self.filename_button_clicked)
|
self.filename_button.clicked.connect(self.filename_button_clicked)
|
||||||
|
|
||||||
self.resize(self.sizeHint())
|
self.resize(self.sizeHint())
|
||||||
|
|
||||||
|
def multiple_box_clicked(self):
|
||||||
|
self.update_filename_box()
|
||||||
|
self.update_icon_filenames_in_box()
|
||||||
|
|
||||||
def update_filename_box(self):
|
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.icon_file_names.sort(key=sort_key)
|
||||||
self.filename_box.addItem('')
|
if doing_multiple:
|
||||||
self.filename_box.addItems(self.icon_file_names)
|
item = QStandardItem(_('Open to see checkboxes'))
|
||||||
|
else:
|
||||||
|
item = QStandardItem('')
|
||||||
|
model.appendRow(item)
|
||||||
|
|
||||||
for i,filename in enumerate(self.icon_file_names):
|
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))
|
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):
|
def update_color_label(self):
|
||||||
pal = QApplication.palette()
|
pal = QApplication.palette()
|
||||||
@ -432,9 +456,9 @@ class RuleEditor(QDialog): # {{{
|
|||||||
all_files=False, select_only_single_file=True)
|
all_files=False, select_only_single_file=True)
|
||||||
if path:
|
if path:
|
||||||
icon_path = path[0]
|
icon_path = path[0]
|
||||||
icon_name = sanitize_file_name_unicode(
|
icon_name = lower(sanitize_file_name_unicode(
|
||||||
os.path.splitext(
|
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:
|
if icon_name not in self.icon_file_names:
|
||||||
self.icon_file_names.append(icon_name)
|
self.icon_file_names.append(icon_name)
|
||||||
self.update_filename_box()
|
self.update_filename_box()
|
||||||
@ -449,13 +473,47 @@ class RuleEditor(QDialog): # {{{
|
|||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
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()
|
self.filename_box.adjustSize()
|
||||||
except:
|
except:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return
|
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):
|
def add_blank_condition(self):
|
||||||
c = ConditionEditor(self.fm, parent=self.conditions_widget)
|
c = ConditionEditor(self.fm, parent=self.conditions_widget)
|
||||||
self.conditions.append(c)
|
self.conditions.append(c)
|
||||||
@ -469,12 +527,11 @@ class RuleEditor(QDialog): # {{{
|
|||||||
self.color_box.setCurrentIndex(idx)
|
self.color_box.setCurrentIndex(idx)
|
||||||
else:
|
else:
|
||||||
self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1)
|
self.kind_box.setCurrentIndex(0 if kind == 'icon' else 1)
|
||||||
if rule.color:
|
self.rule_icon_files = [ic.strip() for ic in rule.color.split(':')]
|
||||||
idx = self.filename_box.findText(rule.color)
|
if len(self.rule_icon_files) > 1:
|
||||||
if idx >= 0:
|
self.multiple_icon_cb.setChecked(True)
|
||||||
self.filename_box.setCurrentIndex(idx)
|
self.update_filename_box()
|
||||||
else:
|
self.update_icon_filenames_in_box()
|
||||||
self.filename_box.setCurrentIndex(0)
|
|
||||||
|
|
||||||
for i in range(self.column_box.count()):
|
for i in range(self.column_box.count()):
|
||||||
c = unicode(self.column_box.itemData(i).toString())
|
c = unicode(self.column_box.itemData(i).toString())
|
||||||
@ -492,10 +549,9 @@ class RuleEditor(QDialog): # {{{
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
if self.rule_kind != 'color':
|
if self.rule_kind != 'color':
|
||||||
fname = lower(unicode(self.filename_box.currentText()))
|
fname = self.get_filenames_from_box()
|
||||||
if not fname:
|
if not fname:
|
||||||
error_dialog(self, _('No icon selected'),
|
error_dialog(self, _('No icon selected'),
|
||||||
_('You must choose an icon for this rule'), show=True)
|
_('You must choose an icon for this rule'), show=True)
|
||||||
@ -528,7 +584,7 @@ class RuleEditor(QDialog): # {{{
|
|||||||
def rule(self):
|
def rule(self):
|
||||||
r = Rule(self.fm)
|
r = Rule(self.fm)
|
||||||
if self.rule_kind != 'color':
|
if self.rule_kind != 'color':
|
||||||
r.color = unicode(self.filename_box.currentText())
|
r.color = self.get_filenames_from_box()
|
||||||
else:
|
else:
|
||||||
r.color = unicode(self.color_box.currentText())
|
r.color = unicode(self.color_box.currentText())
|
||||||
idx = self.column_box.currentIndex()
|
idx = self.column_box.currentIndex()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user