Add box-based search interface tab to advanced search.

This commit is contained in:
Charles Haley 2010-11-08 15:34:14 +00:00
parent f550395add
commit b6df943eb3
4 changed files with 431 additions and 180 deletions

View File

@ -1,17 +1,68 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
import re import re
from PyQt4.QtGui import QDialog from PyQt4.QtGui import QDialog, QDialogButtonBox
from PyQt4 import QtCore
from calibre.gui2.dialogs.search_ui import Ui_Dialog from calibre.gui2.dialogs.search_ui import Ui_Dialog
from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH from calibre.library.caches import CONTAINS_MATCH, EQUALS_MATCH
class SearchDialog(QDialog, Ui_Dialog): class SearchDialog(QDialog, Ui_Dialog):
def __init__(self, *args): def __init__(self, parent, db, box_values, current_tab):
QDialog.__init__(self, *args) QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
self.mc = '' self.mc = ''
searchables = sorted(db.field_metadata.searchable_fields(),
lambda x, y: cmp(x if x[0] != '#' else x[1:],
y if y[0] != '#' else y[1:]))
self.general_combo.addItems(searchables)
if (box_values):
for k,v in box_values.items():
if k == 'general_index':
continue
getattr(self, k).setText(v)
self.general_combo.setCurrentIndex(
self.general_combo.findText(box_values['general_index']))
self.box_last_values = box_values
self.buttonBox.accepted.connect(self.advanced_search_button_pushed)
self.tab_2_button_box.accepted.connect(self.box_search_accepted)
self.tab_2_button_box.rejected.connect(self.box_search_rejected)
self.clear_button.clicked.connect(self.clear_button_pushed)
self.adv_search_used = False
self.box_search_used = False
self.tabWidget.setCurrentIndex(current_tab)
self.tabWidget.currentChanged[int].connect(self.tab_changed)
self.tab_changed(current_tab)
def tab_changed(self, idx):
if idx == 1:
self.tab_2_button_box.button(QDialogButtonBox.Ok).setDefault(True)
else:
self.buttonBox.button(QDialogButtonBox.Ok).setDefault(True)
def advanced_search_button_pushed(self):
self.adv_search_used = True
self.current_tab = 0
QDialog.accept(self)
def box_search_accepted(self):
self.box_search_used = True
self.current_tab = 1
QDialog.accept(self)
def box_search_rejected(self):
QDialog.reject(self)
def clear_button_pushed(self):
self.title_box.setText('')
self.authors_box.setText('')
self.series_box.setText('')
self.tags_box.setText('')
self.general_box.setText('')
def tokens(self, raw): def tokens(self, raw):
phrases = re.findall(r'\s*".*?"\s*', raw) phrases = re.findall(r'\s*".*?"\s*', raw)
@ -21,6 +72,12 @@ class SearchDialog(QDialog, Ui_Dialog):
return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]] return ['"' + self.mc + t + '"' for t in phrases + [r.strip() for r in raw.split()]]
def search_string(self): def search_string(self):
if self.adv_search_used:
return self.adv_search_string()
else:
return self.box_search_string()
def adv_search_string(self):
mk = self.matchkind.currentIndex() mk = self.matchkind.currentIndex()
if mk == CONTAINS_MATCH: if mk == CONTAINS_MATCH:
self.mc = '' self.mc = ''
@ -56,3 +113,34 @@ class SearchDialog(QDialog, Ui_Dialog):
tok = '"%s"'%tok tok = '"%s"'%tok
return tok return tok
def box_search_string(self):
ans = []
self.box_last_values = {}
title = unicode(self.title_box.text()).strip()
self.box_last_values['title_box'] = title
if title:
ans.append('title:"' + title + '"')
author = unicode(self.authors_box.text()).strip()
self.box_last_values['authors_box'] = author
if author:
ans.append('author:"' + author + '"')
series = unicode(self.series_box.text()).strip()
self.box_last_values['series_box'] = series
if series:
ans.append('series:"' + series + '"')
self.mc = '='
tags = unicode(self.tags_box.text())
self.box_last_values['tags_box'] = tags
tags = self.tokens(tags)
if tags:
tags = ['tags:' + t for t in tags]
ans.append('(' + ' or '.join(tags) + ')')
general = unicode(self.general_box.text())
self.box_last_values['general_box'] = general
general_index = unicode(self.general_combo.currentText())
self.box_last_values['general_index'] = general_index
if general:
ans.append(unicode(self.general_combo.currentText()) + ':"' + general + '"')
if ans:
return ' and '.join(ans)
return ''

View File

@ -1,195 +1,352 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class> <class>Dialog</class>
<widget class="QDialog" name="Dialog" > <widget class="QDialog" name="Dialog">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>667</width> <width>686</width>
<height>391</height> <height>360</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string>Advanced Search</string> <string>Advanced Search</string>
</property> </property>
<property name="windowIcon" > <property name="windowIcon">
<iconset resource="../../../../resources/images.qrc" > <iconset>
<normaloff>:/images/search.png</normaloff>:/images/search.png</iconset> <normaloff>:/images/search.png</normaloff>:/images/search.png</iconset>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3" > <layout class="QVBoxLayout" name="verticalLayout_3">
<item> <item>
<widget class="QGroupBox" name="groupBox" > <widget class="QTabWidget" name="tabWidget">
<property name="title" > <property name="currentIndex">
<string>Find entries that have...</string> <number>1</number>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" > <widget class="QWidget" name="tab">
<item> <attribute name="title">
<layout class="QHBoxLayout" name="horizontalLayout" > <string>A&amp;dvanced Search</string>
<item> </attribute>
<widget class="QLabel" name="label" > <layout class="QVBoxLayout" name="verticalLayout_5">
<property name="text" > <item>
<string>&amp;All these words:</string> <widget class="QGroupBox" name="groupBox">
</property> <property name="title">
<property name="buddy" > <string>Find entries that have...</string>
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="all" />
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" >
<item>
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>This exact &amp;phrase:</string>
</property>
<property name="buddy" >
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="phrase" />
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3" >
<item>
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>&amp;One or more of these words:</string>
</property>
<property name="buddy" >
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="any" />
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2" >
<property name="title" >
<string>But dont show entries that have...</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4" >
<item>
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Any of these &amp;unwanted words:</string>
</property>
<property name="buddy" >
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="none" />
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5" >
<item>
<widget class="QLabel" name="label_5" >
<property name="text" >
<string>What kind of match to use:</string>
</property>
<property name="buddy" >
<cstring>matchkind</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="matchkind">
<item>
<property name="text">
<string>Contains: the word or phrase matches anywhere in the metadata</string>
</property> </property>
</item> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<property name="text"> <layout class="QHBoxLayout" name="horizontalLayout">
<string>Equals: the word or phrase must match an entire metadata field</string> <item>
<widget class="QLabel" name="label">
<property name="text">
<string>&amp;All these words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="all"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>This exact &amp;phrase:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="phrase"/>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>&amp;One or more of these words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="any"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>But dont show entries that have...</string>
</property> </property>
</item> <layout class="QVBoxLayout" name="verticalLayout_2">
<item> <item>
<property name="text"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<string>Regular expression: the expression must match anywhere in the metadata</string> <item>
<widget class="QLabel" name="label_4">
<property name="text">
<string>Any of these &amp;unwanted words:</string>
</property>
<property name="buddy">
<cstring>all</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="none"/>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_5">
<property name="text">
<string>What kind of match to use:</string>
</property>
<property name="buddy">
<cstring>matchkind</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="matchkind">
<item>
<property name="text">
<string>Contains: the word or phrase matches anywhere in the metadata</string>
</property>
</item>
<item>
<property name="text">
<string>Equals: the word or phrase must match an entire metadata field</string>
</property>
</item>
<item>
<property name="text">
<string>Regular expression: the expression must match anywhere in the metadata</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_51">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>40</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="buddy">
<cstring>matchkind</cstring>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_6">
<property name="maximumSize">
<size>
<width>16777215</width>
<height>30</height>
</size>
</property>
<property name="text">
<string>See the &lt;a href=&quot;http://calibre-ebook.com/user_manual/gui.html#the-search-interface&quot;&gt;User Manual&lt;/a&gt; for more help</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property> </property>
</item> <property name="standardButtons">
</widget> <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</item> </property>
<item> </widget>
<widget class="QLabel" name="label_51" > </item>
<property name="sizePolicy"> <item>
<sizepolicy hsizetype="Preferred" vsizetype="Preferred"> <spacer name="verticalSpacer_2">
<horstretch>40</horstretch> <property name="orientation">
<verstretch>0</verstretch> <enum>Qt::Vertical</enum>
</sizepolicy> </property>
</property> <property name="sizeHint" stdset="0">
<property name="text" > <size>
<string> </string> <width>20</width>
</property> <height>40</height>
<property name="buddy" > </size>
<cstring>matchkind</cstring> </property>
</property> </spacer>
</widget> </item>
</item> </layout>
</layout> </widget>
</widget> <widget class="QWidget" name="tab_2">
</item> <attribute name="title">
<item> <string>Titl&amp;e/Author/Series ...</string>
<widget class="QLabel" name="label_6" > </attribute>
<property name="maximumSize" > <layout class="QGridLayout" name="gridLayout">
<size> <item row="0" column="0">
<width>16777215</width> <widget class="QLabel" name="label_7">
<height>30</height> <property name="text">
</size> <string>&amp;Title:</string>
</property> </property>
<property name="text" > <property name="buddy">
<string>See the &lt;a href="http://calibre-ebook.com/user_manual/gui.html#the-search-interface">User Manual&lt;/a> for more help</string> <cstring>title_box</cstring>
</property> </property>
<property name="openExternalLinks" > </widget>
<bool>true</bool> </item>
</property> <item row="0" column="1">
</widget> <widget class="QLineEdit" name="title_box">
</item> <property name="toolTip">
<item> <string>Enter the title.</string>
<widget class="QDialogButtonBox" name="buttonBox" > </property>
<property name="orientation" > </widget>
<enum>Qt::Horizontal</enum> </item>
</property> <item row="1" column="0">
<property name="standardButtons" > <widget class="QLabel" name="label_8">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> <property name="text">
</property> <string>&amp;Author</string>
</property>
<property name="buddy">
<cstring>authors_box</cstring>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>&amp;Series</string>
</property>
<property name="buddy">
<cstring>series_box</cstring>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Ta&amp;gs</string>
</property>
<property name="buddy">
<cstring>tags_box</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="authors_box">
<property name="toolTip">
<string>Enter an author's name. Only one author can be used.</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="series_box">
<property name="toolTip">
<string>Enter a series name, without an index. Only one series name can be used.</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="tags_box">
<property name="toolTip">
<string>Enter tags separated by spaces</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QLineEdit" name="general_box"/>
</item>
<item row="7" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="5" column="0">
<widget class="QComboBox" name="general_combo"/>
</item>
<item row="6" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="clear_button">
<property name="text">
<string>&amp;Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="tab_2_button_box">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget> </widget>
</item> </item>
</layout> </layout>
</widget> </widget>
<tabstops>
<tabstop>all</tabstop>
<tabstop>phrase</tabstop>
<tabstop>any</tabstop>
<tabstop>none</tabstop>
<tabstop>matchkind</tabstop>
<tabstop>buttonBox</tabstop>
<tabstop>title_box</tabstop>
<tabstop>authors_box</tabstop>
<tabstop>series_box</tabstop>
<tabstop>tags_box</tabstop>
<tabstop>general_combo</tabstop>
<tabstop>general_box</tabstop>
<tabstop>clear_button</tabstop>
<tabstop>tab_2_button_box</tabstop>
<tabstop>tabWidget</tabstop>
</tabstops>
<resources> <resources>
<include location="../../../../resources/images.qrc" /> <include location="../../../CBH_Data/calibre_development/calibre/resources/images.qrc"/>
</resources> </resources>
<connections> <connections>
<connection> <connection>
@ -198,11 +355,11 @@
<receiver>Dialog</receiver> <receiver>Dialog</receiver>
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel">
<x>248</x> <x>248</x>
<y>254</y> <y>254</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel">
<x>157</x> <x>157</x>
<y>274</y> <y>274</y>
</hint> </hint>
@ -214,11 +371,11 @@
<receiver>Dialog</receiver> <receiver>Dialog</receiver>
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel">
<x>316</x> <x>316</x>
<y>260</y> <y>260</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel">
<x>286</x> <x>286</x>
<y>274</y> <y>274</y>
</hint> </hint>

View File

@ -167,6 +167,7 @@ class SearchBar(QWidget): # {{{
x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) x.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
parent.advanced_search_button = x = QToolButton(self) parent.advanced_search_button = x = QToolButton(self)
parent.advanced_search_button.setShortcut(_("Ctrl+s"))
x.setIcon(QIcon(I('search.png'))) x.setIcon(QIcon(I('search.png')))
l.addWidget(x) l.addWidget(x)
x.setToolTip(_("Advanced search")) x.setToolTip(_("Advanced search"))

View File

@ -381,6 +381,8 @@ class SearchBoxMixin(object):
unicode(self.search.toolTip()))) unicode(self.search.toolTip())))
self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip()) self.advanced_search_button.setStatusTip(self.advanced_search_button.toolTip())
self.clear_button.setStatusTip(self.clear_button.toolTip()) self.clear_button.setStatusTip(self.clear_button.toolTip())
self.search_last_values = None
self.search_current_tab = 0
def search_box_cleared(self): def search_box_cleared(self):
self.tags_view.clear() self.tags_view.clear()
@ -392,9 +394,12 @@ class SearchBoxMixin(object):
self.tags_view.clear() self.tags_view.clear()
def do_advanced_search(self, *args): def do_advanced_search(self, *args):
d = SearchDialog(self) d = SearchDialog(self, self.library_view.model().db,
self.search_last_values, self.search_current_tab)
if d.exec_() == QDialog.Accepted: if d.exec_() == QDialog.Accepted:
self.search.set_search_string(d.search_string()) self.search.set_search_string(d.search_string())
self.search_last_values = d.box_last_values
self.search_current_tab = d.current_tab
class SavedSearchBoxMixin(object): class SavedSearchBoxMixin(object):