KG updates

This commit is contained in:
GRiker 2010-11-28 04:50:56 -07:00
commit 66e4d7a56f
14 changed files with 140 additions and 44 deletions

View File

@ -13,6 +13,8 @@ class RevistaMuyInteresante(BasicNewsRecipe):
no_stylesheets = True
remove_javascript = True
conversion_options = {'linearize_tables': True}
extra_css = ' .txt_articulo{ font-family: sans-serif; font-size: medium; text-align: justify } .contentheading{font-family: serif; font-size: large; font-weight: bold; color: #000000; text-align: center}'
@ -39,11 +41,12 @@ class RevistaMuyInteresante(BasicNewsRecipe):
keep_only_tags = [dict(name='div', attrs={'class':['article']}),dict(name='td', attrs={'class':['txt_articulo']})]
remove_tags = [
dict(name=['object','link','script','ul'])
dict(name=['object','link','script','ul','iframe','ins'])
,dict(name='div', attrs={'id':['comment']})
,dict(name='td', attrs={'class':['buttonheading']})
,dict(name='div', attrs={'class':['tags_articles']})
,dict(name='div', attrs={'class':['tags_articles','bajo_title']})
,dict(name='table', attrs={'class':['pagenav']})
,dict(name='form', attrs={'class':['voteform']})
]
remove_tags_after = dict(name='div', attrs={'class':'tags_articles'})
@ -115,3 +118,5 @@ class RevistaMuyInteresante(BasicNewsRecipe):
if link_item:
cover_url = "http://www.muyinteresante.es"+link_item['src']
return cover_url

View File

@ -370,6 +370,15 @@ class InterfaceActionBase(Plugin): # {{{
can_be_disabled = False
actual_plugin = None
def load_actual_plugin(self, gui):
'''
This method must return the actual interface action plugin object.
'''
mod, cls = self.actual_plugin.split(':')
return getattr(__import__(mod, fromlist=['1'], level=0), cls)(gui,
self.site_customization)
# }}}
class PreferencesPlugin(Plugin): # {{{

View File

@ -292,7 +292,8 @@ class RTFInput(InputFormatPlugin):
# Replace newlines inserted by the 'empty_paragraphs' option in rtf2xml with html blank lines
if not getattr(self.options, 'remove_paragraph_spacing', False):
res = re.sub('\s*<body>', '<body>', res)
res = re.sub('(?<=\n)\n{2}', u'<p>\u00a0</p>\n', res)
res = re.sub('(?<=\n)\n{2}',
u'<p>\u00a0</p>\n'.encode('utf-8'), res)
if self.options.preprocess_html:
preprocessor = PreProcessor(self.options, log=getattr(self, 'log', None))
res = preprocessor(res)

View File

@ -214,9 +214,18 @@ class BookInfo(QWebView):
def _show_data(self, rows, comments):
def color_to_string(col):
ans = '#000000'
if col.isValid():
col = col.toRgb()
if col.isValid():
ans = unicode(col.name())
return ans
f = QFontInfo(QApplication.font(self.parent())).pixelSize()
c = unicode(QApplication.palette().color(QPalette.Normal,
QPalette.WindowText).name())
c = color_to_string(QApplication.palette().color(QPalette.Normal,
QPalette.WindowText))
templ = u'''\
<html>
<head>

View File

@ -437,7 +437,7 @@ class BulkBool(BulkBase, Bool):
if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None:
val = False
if value is not None and value != val:
return None
return 'nochange'
value = val
return value
@ -445,19 +445,23 @@ class BulkBool(BulkBase, Bool):
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
QComboBox(parent)]
w = self.widgets[1]
items = [_('Yes'), _('No'), _('Undefined')]
icons = [I('ok.png'), I('list_remove.png'), I('blank.png')]
items = [_('Yes'), _('No'), _('Undefined'), _('Do not change')]
icons = [I('ok.png'), I('list_remove.png'), I('blank.png'), I('blank.png')]
for icon, text in zip(icons, items):
w.addItem(QIcon(icon), text)
def getter(self):
val = self.widgets[1].currentIndex()
return {3: 'nochange', 2: None, 1: False, 0: True}[val]
def setter(self, val):
val = {None: 2, False: 1, True: 0}[val]
val = {'nochange': 3, None: 2, False: 1, True: 0}[val]
self.widgets[1].setCurrentIndex(val)
def commit(self, book_ids, notify=False):
val = self.gui_val
val = self.normalize_ui_val(val)
if val != self.initial_val:
if val != self.initial_val and val != 'nochange':
if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None:
val = False
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)

View File

@ -102,9 +102,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
self.device_connected = None
acmap = OrderedDict()
for action in interface_actions():
mod, cls = action.actual_plugin.split(':')
ac = getattr(__import__(mod, fromlist=['1'], level=0), cls)(self,
action.site_customization)
ac = action.load_actual_plugin(self)
if ac.name in acmap:
if ac.priority >= acmap[ac.name].priority:
acmap[ac.name] = ac

View File

@ -403,7 +403,7 @@ class ResultCache(SearchQueryParser): # {{{
'<=':[2, lambda r, q: r <= q]
}
def get_numeric_matches(self, location, query):
def get_numeric_matches(self, location, query, val_func = None):
matches = set([])
if len(query) == 0:
return matches
@ -419,7 +419,10 @@ class ResultCache(SearchQueryParser): # {{{
if relop is None:
(p, relop) = self.numeric_search_relops['=']
loc = self.field_metadata[location]['rec_index']
if val_func is None:
loc = self.field_metadata[location]['rec_index']
val_func = lambda item, loc=loc: item[loc]
dt = self.field_metadata[location]['datatype']
if dt == 'int':
cast = (lambda x: int (x))
@ -430,6 +433,9 @@ class ResultCache(SearchQueryParser): # {{{
elif dt == 'float':
cast = lambda x : float (x)
adjust = lambda x: x
else: # count operation
cast = (lambda x: int (x))
adjust = lambda x: x
if len(query) > 1:
mult = query[-1:].lower()
@ -446,10 +452,11 @@ class ResultCache(SearchQueryParser): # {{{
for item in self._data:
if item is None:
continue
if not item[loc]:
v = val_func(item)
if not v:
i = 0
else:
i = adjust(item[loc])
i = adjust(v)
if relop(i, q):
matches.add(item[0])
return matches
@ -467,15 +474,23 @@ class ResultCache(SearchQueryParser): # {{{
return matches
raise ParseException(query, len(query), 'Recursive query group detected', self)
# take care of dates special case
if location in self.field_metadata and \
self.field_metadata[location]['datatype'] == 'datetime':
return self.get_dates_matches(location, query.lower())
if location in self.field_metadata:
fm = self.field_metadata[location]
# take care of dates special case
if fm['datatype'] == 'datetime':
return self.get_dates_matches(location, query.lower())
# take care of numbers special case
if location in self.field_metadata and \
self.field_metadata[location]['datatype'] in ('rating', 'int', 'float'):
return self.get_numeric_matches(location, query.lower())
# take care of numbers special case
if fm['datatype'] in ('rating', 'int', 'float'):
return self.get_numeric_matches(location, query.lower())
# take care of the 'count' operator for is_multiples
if fm['is_multiple'] and \
len(query) > 1 and query.startswith('#') and \
query[1:1] in '=<>!':
vf = lambda item, loc=fm['rec_index'], ms=fm['is_multiple']:\
len(item[loc].split(ms)) if item[loc] is not None else 0
return self.get_numeric_matches(location, query[1:], val_func=vf)
# everything else, or 'all' matches
matchkind = CONTAINS_MATCH

View File

@ -268,8 +268,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
base,
prefer_custom=True)
self.field_metadata.set_field_record_index('cover',
self.FIELD_MAP['cover'], prefer_custom=False)
self.FIELD_MAP['ondevice'] = base+1
self.field_metadata.set_field_record_index('ondevice', base+1, prefer_custom=False)
self.FIELD_MAP['all_metadata'] = base+2

View File

@ -3,6 +3,7 @@ Created on 25 May 2010
@author: charles
'''
import copy
from calibre.utils.ordered_dict import OrderedDict
from calibre.utils.config import tweaks
@ -86,7 +87,7 @@ class FieldMetadata(dict):
# Builtin metadata {{{
_field_metadata = [
_field_metadata_prototype = [
('authors', {'table':'authors',
'column':'name',
'link_column':'author',
@ -161,6 +162,15 @@ class FieldMetadata(dict):
'search_terms':['tags', 'tag'],
'is_custom':False,
'is_category':True}),
('all_metadata',{'table':None,
'column':None,
'datatype':None,
'is_multiple':None,
'kind':'field',
'name':None,
'search_terms':[],
'is_custom':False,
'is_category':False}),
('author_sort',{'table':None,
'column':None,
'datatype':'text',
@ -180,7 +190,7 @@ class FieldMetadata(dict):
'is_custom':False, 'is_category':False}),
('cover', {'table':None,
'column':None,
'datatype':None,
'datatype':'int',
'is_multiple':None,
'kind':'field',
'name':None,
@ -223,15 +233,6 @@ class FieldMetadata(dict):
'search_terms':[],
'is_custom':False,
'is_category':False}),
('all_metadata',{'table':None,
'column':None,
'datatype':None,
'is_multiple':None,
'kind':'field',
'name':None,
'search_terms':[],
'is_custom':False,
'is_category':False}),
('ondevice', {'table':None,
'column':None,
'datatype':'text',
@ -322,6 +323,7 @@ class FieldMetadata(dict):
]
def __init__(self):
self._field_metadata = copy.deepcopy(self._field_metadata_prototype)
self._tb_cats = OrderedDict()
self._search_term_map = {}
self.custom_label_to_key_map = {}

View File

@ -98,6 +98,44 @@ Every time you use calibre to convert a book, the plugin's :meth:`run` method wi
converted book will have its publisher set to "Hello World". For more information about
|app|'s plugin system, read on...
A Hello World GUI plugin
---------------------------
Here's a simple Hello World plugin for the |app| GUI. It will cause a box to popup with the message "Hellooo World!" when you press Ctrl+Shift+H
.. code-block:: python
from calibre.customize import InterfaceActionBase
class HelloWorldBase(InterfaceActionBase):
name = 'Hello World GUI'
author = 'The little green man'
def load_actual_plugin(self, gui):
from calibre.gui2.actions import InterfaceAction
class HelloWorld(InterfaceAction):
name = 'Hello World GUI'
action_spec = ('Hello World!', 'add_book.png', None,
_('Ctrl+Shift+H'))
def genesis(self):
self.qaction.triggered.connect(self.hello_world)
def hello_world(self, *args):
from calibre.gui2 import info_dialog
info_dialog(self.gui, 'Hello World!', 'Hellooo World!',
show=True)
return HelloWorld(gui, self.site_customization)
You can also have it show up in the toolbars/context menu by going to Preferences->Toolbars and adding this plugin to the locations you want it to be in.
While this plugin is utterly useless, note that all calibre GUI actions like adding/saving/removing/viewing/etc. are implemented as plugins, so there is no limit to what you can achieve. The key thing to remember is that the plugin has access to the full |app| GUI via ``self.gui``.
The Plugin base class
------------------------

View File

@ -274,6 +274,14 @@ Searching for ``no`` or ``unchecked`` will find all books with ``No`` in the col
:guilabel:`Advanced Search Dialog`
You can test for the number of items in multiple-value columns, such as tags, formats, authors, and tags-like custom columns. This is done using a syntax very similar to numeric tests (discussed above), except that the relational operator begins with a ``#`` character. For example::
tags:#>3 will give you books with more than three tags
tags:#!=3 will give you books that do not have three tags
authors:#=1 will give you books with exactly one author
#cust:#<5 will give you books with less than five items in custom column #cust
formats:#>1 will give you books with more than one format
Saving searches
-----------------

View File

@ -161,11 +161,20 @@ The base class for such devices is :class:`calibre.devices.usbms.driver.USBMS`.
User Interface Actions
--------------------------
If you are adding your own plugin in a zip file, you should subclass both InterfaceActionBase and InterfaceAction. The :meth:`load_actual_plugin` method of you InterfaceActionBase subclass must return an instantiated object of your InterfaceBase subclass.
.. autoclass:: calibre.gui2.actions.InterfaceAction
:show-inheritance:
:members:
:member-order: bysource
.. autoclass:: calibre.customize.InterfaceActionBase
:show-inheritance:
:members:
:member-order: bysource
Preferences Plugins
--------------------------