Store: Advance search for affiliate. Clean up tool tips. Add quick enable buttons in chooser. Add affiliate status in chooser.

This commit is contained in:
John Schember 2011-05-28 16:55:57 -04:00
parent 66315cead2
commit a46954edf9
11 changed files with 118 additions and 25 deletions

View File

@ -45,8 +45,9 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
self.description_box.setText('') self.description_box.setText('')
self.headquarters_box.setText('') self.headquarters_box.setText('')
self.format_box.setText('') self.format_box.setText('')
self.enabled_combo.setIndex(0) self.enabled_combo.setCurrentIndex(0)
self.drm_combo.setIndex(0) self.drm_combo.setCurrentIndex(0)
self.affiliate_combo.setCurrentIndex(0)
def tokens(self, raw): def tokens(self, raw):
phrases = re.findall(r'\s*".*?"\s*', raw) phrases = re.findall(r'\s*".*?"\s*', raw)
@ -126,6 +127,9 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
drm = unicode(self.drm_combo.currentText()).strip() drm = unicode(self.drm_combo.currentText()).strip()
if drm: if drm:
ans.append('drm:' + drm) ans.append('drm:' + drm)
affiliate = unicode(self.affiliate_combo.currentText()).strip()
if affiliate:
ans.append('affiliate:' + affiliate)
if ans: if ans:
return ' and '.join(ans) return ' and '.join(ans)
return '' return ''

View File

@ -226,7 +226,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="0" colspan="2"> <item row="9" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>
<widget class="QPushButton" name="clear_button"> <widget class="QPushButton" name="clear_button">
@ -244,7 +244,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -335,6 +335,32 @@
</item> </item>
</widget> </widget>
</item> </item>
<item row="7" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>Affiliate:</string>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QComboBox" name="affiliate_combo">
<item>
<property name="text">
<string/>
</property>
</item>
<item>
<property name="text">
<string>true</string>
</property>
</item>
<item>
<property name="text">
<string>false</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

View File

@ -23,6 +23,9 @@ class StoreChooserWidget(QWidget, Ui_Form):
self.search.clicked.connect(self.do_search) self.search.clicked.connect(self.do_search)
self.adv_search_builder.clicked.connect(self.build_adv_search) self.adv_search_builder.clicked.connect(self.build_adv_search)
self.enable_all.clicked.connect(self.results_view.model().enable_all)
self.enable_none.clicked.connect(self.results_view.model().enable_none)
self.enable_invert.clicked.connect(self.results_view.model().enable_invert)
self.results_view.activated.connect(self.toggle_plugin) self.results_view.activated.connect(self.toggle_plugin)
def do_search(self): def do_search(self):

View File

@ -85,26 +85,26 @@
<item> <item>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Select:</string> <string>Enable</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="select_all"> <widget class="QPushButton" name="enable_all">
<property name="text"> <property name="text">
<string>All</string> <string>All</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="select_none"> <widget class="QPushButton" name="enable_none">
<property name="text"> <property name="text">
<string>None</string> <string>None</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="select_invert"> <widget class="QPushButton" name="enable_invert">
<property name="text"> <property name="text">
<string>Invert</string> <string>Invert</string>
</property> </property>

View File

@ -6,7 +6,7 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>' __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex) from PyQt4.Qt import (Qt, QAbstractItemModel, QIcon, QVariant, QModelIndex, QSize)
from calibre.gui2 import NONE from calibre.gui2 import NONE
from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin from calibre.customize.ui import is_disabled, disable_plugin, enable_plugin
@ -18,13 +18,15 @@ from calibre.utils.search_query_parser import SearchQueryParser
class Matches(QAbstractItemModel): class Matches(QAbstractItemModel):
HEADERS = [_('Enabled'), _('Name'), _('No DRM'), _('Headquarters'), _('Formats')] HEADERS = [_('Enabled'), _('Name'), _('No DRM'), _('Headquarters'), _('Affiliate'), _('Formats')]
HTML_COLS = [1] HTML_COLS = [1]
def __init__(self, plugins): def __init__(self, plugins):
QAbstractItemModel.__init__(self) QAbstractItemModel.__init__(self)
self.NO_DRM_ICON = QIcon(I('ok.png')) self.NO_DRM_ICON = QIcon(I('ok.png'))
self.DONATE_ICON = QIcon()
self.DONATE_ICON.addFile(I('donate.png'), QSize(16, 16))
self.all_matches = plugins self.all_matches = plugins
self.matches = plugins self.matches = plugins
@ -53,6 +55,22 @@ class Matches(QAbstractItemModel):
self.layoutChanged.emit() self.layoutChanged.emit()
self.sort(self.sort_col, self.sort_order) self.sort(self.sort_col, self.sort_order)
def enable_all(self):
for i in xrange(len(self.matches)):
index = self.createIndex(i, 0)
data = QVariant(True)
self.setData(index, data, Qt.CheckStateRole)
def enable_none(self):
for i in xrange(len(self.matches)):
index = self.createIndex(i, 0)
data = QVariant(False)
self.setData(index, data, Qt.CheckStateRole)
def enable_invert(self):
for i in xrange(len(self.matches)):
self.toggle_plugin(self.createIndex(i, 0))
def toggle_plugin(self, index): def toggle_plugin(self, index):
new_index = self.createIndex(index.row(), 0) new_index = self.createIndex(index.row(), 0)
data = QVariant(is_disabled(self.get_plugin(index))) data = QVariant(is_disabled(self.get_plugin(index)))
@ -91,12 +109,15 @@ class Matches(QAbstractItemModel):
return QVariant('<b>%s</b><br><i>%s</i>' % (result.name, result.description)) return QVariant('<b>%s</b><br><i>%s</i>' % (result.name, result.description))
elif col == 3: elif col == 3:
return QVariant(result.headquarters) return QVariant(result.headquarters)
elif col == 4: elif col == 5:
return QVariant(', '.join(result.formats).upper()) return QVariant(', '.join(result.formats).upper())
elif role == Qt.DecorationRole: elif role == Qt.DecorationRole:
if col == 2: if col == 2:
if result.drm_free_only: if result.drm_free_only:
return QVariant(self.NO_DRM_ICON) return QVariant(self.NO_DRM_ICON)
if col == 4:
if result.affiliate:
return QVariant(self.DONATE_ICON)
elif role == Qt.CheckStateRole: elif role == Qt.CheckStateRole:
if col == 0: if col == 0:
if is_disabled(result): if is_disabled(result):
@ -105,20 +126,23 @@ class Matches(QAbstractItemModel):
elif role == Qt.ToolTipRole: elif role == Qt.ToolTipRole:
if col == 0: if col == 0:
if is_disabled(result): if is_disabled(result):
return QVariant(_('<p>This store is currently diabled and cannot be used in other parts of calibre.</p>')) return QVariant('<p>' + _('This store is currently diabled and cannot be used in other parts of calibre.') + '</p>')
else: else:
return QVariant(_('<p>This store is currently enabled and can be used in other parts of calibre.</p>')) return QVariant('<p>' + _('This store is currently enabled and can be used in other parts of calibre.') + '</p>')
elif col == 1: elif col == 1:
return QVariant('<p>%s</p>' % result.description) return QVariant('<p>%s</p>' % result.description)
elif col == 2: elif col == 2:
if result.drm_free_only: if result.drm_free_only:
return QVariant(_('<p>This store only distributes ebooks with DRM.</p>')) return QVariant('<p>' + _('This store only distributes ebooks with DRM.') + '</p>')
else: else:
return QVariant(_('<p>This store distributes ebooks with DRM. It may have some titles without DRM, but you will need to check on a per title basis.</p>')) return QVariant('<p>' + _('This store distributes ebooks with DRM. It may have some titles without DRM, but you will need to check on a per title basis.') + '</p>')
elif col == 3: elif col == 3:
return QVariant(_('<p>This store is headquartered in %s. This is a good indication of what market the store caters to. However, this does not necessarily mean that the store is limited to that market only.</p>') % result.headquarters) return QVariant('<p>' + _('This store is headquartered in %s. This is a good indication of what market the store caters to. However, this does not necessarily mean that the store is limited to that market only.') % result.headquarters + '</p>')
elif col == 4: elif col == 4:
return QVariant(_('<p>This store distributes ebooks in the following formats: %s</p>') % ', '.join(result.formats)) if result.affiliate:
return QVariant('<p>' + _('Buying from this store supports the calibre developer: %s.') % result.author + '</p>')
elif col == 5:
return QVariant('<p>' + _('This store distributes ebooks in the following formats: %s') % ', '.join(result.formats) + '</p>')
return NONE return NONE
def setData(self, index, data, role): def setData(self, index, data, role):
@ -148,6 +172,8 @@ class Matches(QAbstractItemModel):
text = 'a' if getattr(match, 'drm_free_only', True) else 'b' text = 'a' if getattr(match, 'drm_free_only', True) else 'b'
elif col == 3: elif col == 3:
text = getattr(match, 'headquarters', '') text = getattr(match, 'headquarters', '')
elif col == 4:
text = 'a' if getattr(match, 'affiliate', False) else 'b'
return text return text
def sort(self, col, order, reset=True): def sort(self, col, order, reset=True):
@ -167,6 +193,7 @@ class SearchFilter(SearchQueryParser):
USABLE_LOCATIONS = [ USABLE_LOCATIONS = [
'all', 'all',
'affiliate',
'description', 'description',
'drm', 'drm',
'enabled', 'enabled',
@ -207,6 +234,7 @@ class SearchFilter(SearchQueryParser):
all_locs = set(self.USABLE_LOCATIONS) - set(['all']) all_locs = set(self.USABLE_LOCATIONS) - set(['all'])
locations = all_locs if location == 'all' else [location] locations = all_locs if location == 'all' else [location]
q = { q = {
'affiliate': lambda x: x.affiliate,
'description': lambda x: x.description.lower(), 'description': lambda x: x.description.lower(),
'drm': lambda x: not x.drm_free_only, 'drm': lambda x: not x.drm_free_only,
'enabled': lambda x: not is_disabled(x), 'enabled': lambda x: not is_disabled(x),
@ -219,21 +247,21 @@ class SearchFilter(SearchQueryParser):
for locvalue in locations: for locvalue in locations:
accessor = q[locvalue] accessor = q[locvalue]
if query == 'true': if query == 'true':
if locvalue in ('drm', 'enabled'): if locvalue in ('affiliate', 'drm', 'enabled'):
if accessor(sr) == True: if accessor(sr) == True:
matches.add(sr) matches.add(sr)
elif accessor(sr) is not None: elif accessor(sr) is not None:
matches.add(sr) matches.add(sr)
continue continue
if query == 'false': if query == 'false':
if locvalue in ('drm', 'enabled'): if locvalue in ('affiliate', 'drm', 'enabled'):
if accessor(sr) == False: if accessor(sr) == False:
matches.add(sr) matches.add(sr)
elif accessor(sr) is None: elif accessor(sr) is None:
matches.add(sr) matches.add(sr)
continue continue
# this is bool, so can't match below # this is bool, so can't match below
if locvalue in ('drm', 'enabled'): if locvalue in ('affiliate', 'drm', 'enabled'):
continue continue
try: try:
### Can't separate authors because comma is used for name sep and author sep ### Can't separate authors because comma is used for name sep and author sep

View File

@ -45,6 +45,7 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
self.author_box.setText('') self.author_box.setText('')
self.price_box.setText('') self.price_box.setText('')
self.format_box.setText('') self.format_box.setText('')
self.affiliate_combo.setCurrentIndex(0)
def tokens(self, raw): def tokens(self, raw):
phrases = re.findall(r'\s*".*?"\s*', raw) phrases = re.findall(r'\s*".*?"\s*', raw)
@ -118,6 +119,9 @@ class AdvSearchBuilderDialog(QDialog, Ui_Dialog):
format = unicode(self.format_box.text()).strip() format = unicode(self.format_box.text()).strip()
if format: if format:
ans.append('format:"' + self.mc + format + '"') ans.append('format:"' + self.mc + format + '"')
affiliate = unicode(self.affiliate_combo.currentText()).strip()
if affiliate:
ans.append('affiliate:' + affiliate)
if ans: if ans:
return ' and '.join(ans) return ' and '.join(ans)
return '' return ''

View File

@ -226,7 +226,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0" colspan="2"> <item row="7" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_6"> <layout class="QHBoxLayout" name="horizontalLayout_6">
<item> <item>
<widget class="QPushButton" name="clear_button"> <widget class="QPushButton" name="clear_button">
@ -244,7 +244,7 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -283,6 +283,32 @@
<item row="3" column="1"> <item row="3" column="1">
<widget class="EnLineEdit" name="price_box"/> <widget class="EnLineEdit" name="price_box"/>
</item> </item>
<item row="5" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Affiliate:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QComboBox" name="affiliate_combo">
<item>
<property name="text">
<string/>
</property>
</item>
<item>
<property name="text">
<string>true</string>
</property>
</item>
<item>
<property name="text">
<string>false</string>
</property>
</item>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

View File

@ -121,6 +121,7 @@ class SearchThread(Thread):
return return
res.store_name = store_name res.store_name = store_name
res.affiliate = store_plugin.base_plugin.affiliate res.affiliate = store_plugin.base_plugin.affiliate
res.plugin_author = store_plugin.base_plugin.author
self.results.put((res, store_plugin)) self.results.put((res, store_plugin))
self.tasks.task_done() self.tasks.task_done()
except: except:

View File

@ -200,7 +200,7 @@ class Matches(QAbstractItemModel):
return QVariant('<p>%s</p>' % result.formats) return QVariant('<p>%s</p>' % result.formats)
elif col == 5: elif col == 5:
if result.affiliate: if result.affiliate:
return QVariant(_('Buying from this store supports a calibre developer')) return QVariant('<p>' + _('Buying from this store supports the calibre developer: %s.') % result.plugin_author + '</p>')
elif role == Qt.SizeHintRole: elif role == Qt.SizeHintRole:
return QSize(64, 64) return QSize(64, 64)
return NONE return NONE

View File

@ -102,7 +102,7 @@ class SearchDialog(QDialog, Ui_Dialog):
store_list_layout.addWidget(cbox, i, 0, 1, 1) store_list_layout.addWidget(cbox, i, 0, 1, 1)
if self.gui.istores[x].base_plugin.affiliate: if self.gui.istores[x].base_plugin.affiliate:
iw = QLabel(self) iw = QLabel(self)
iw.setToolTip(_('Buying from this store supports a calibre developer')) iw.setToolTip('<p>' + _('Buying from this store supports the calibre developer: %s</p>') % self.gui.istores[x].base_plugin.author + '</p>')
iw.setPixmap(icon.pixmap(16, 16)) iw.setPixmap(icon.pixmap(16, 16))
store_list_layout.addWidget(iw, i, 1, 1, 1) store_list_layout.addWidget(iw, i, 1, 1, 1)
self.store_checks[x] = cbox self.store_checks[x] = cbox

View File

@ -23,6 +23,7 @@ class SearchResult(object):
self.drm = None self.drm = None
self.formats = '' self.formats = ''
self.affiliate = False self.affiliate = False
self.plugin_author = ''
def __eq__(self, other): def __eq__(self, other):
return self.title == other.title and self.author == other.author and self.store_name == other.store_name return self.title == other.title and self.author == other.author and self.store_name == other.store_name