Created a new SearchEdit class, and implemented it in the song search.

bzr-revno: 1172
This commit is contained in:
Raoul Snyman 2010-12-29 18:16:51 +00:00 committed by Tim Bentley
commit 5e730c3138
8 changed files with 226 additions and 32 deletions

View File

@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon
log = logging.getLogger(__name__)
class SearchEdit(QtGui.QLineEdit):
"""
This is a specialised QLineEdit with a "clear" button inside for searches.
"""
def __init__(self, parent):
"""
Constructor.
"""
QtGui.QLineEdit.__init__(self, parent)
self._currentSearchType = -1
self.clearButton = QtGui.QToolButton(self)
self.clearButton.setIcon(build_icon(u':/system/clear_shortcut.png'))
self.clearButton.setCursor(QtCore.Qt.ArrowCursor)
self.clearButton.setStyleSheet(
u'QToolButton { border: none; padding: 0px; }')
self.clearButton.resize(18, 18)
self.clearButton.hide()
QtCore.QObject.connect(
self.clearButton,
QtCore.SIGNAL(u'clicked()'),
self._onClearButtonClicked
)
QtCore.QObject.connect(
self,
QtCore.SIGNAL(u'textChanged(const QString&)'),
self._onSearchEditTextChanged
)
self._updateStyleSheet()
def _updateStyleSheet(self):
"""
Internal method to update the stylesheet depending on which widgets are
available and visible.
"""
frameWidth = self.style().pixelMetric(
QtGui.QStyle.PM_DefaultFrameWidth)
rightPadding = self.clearButton.sizeHint().width() + frameWidth
if hasattr(self, u'menuButton'):
leftPadding = self.menuButton.width()
self.setStyleSheet(
u'QLineEdit { padding-left: %spx; padding-right: %spx; } ' % \
(leftPadding, rightPadding))
else:
self.setStyleSheet(u'QLineEdit { padding-right: %spx; } ' % \
rightPadding)
msz = self.minimumSizeHint();
self.setMinimumSize(
max(msz.width(),
self.clearButton.sizeHint().width() + (frameWidth * 2) + 2),
max(msz.height(),
self.clearButton.height() + (frameWidth * 2) + 2)
)
def resizeEvent(self, event):
"""
Reimplemented method to react to resizing of the widget.
``event``
The event that happened.
"""
sz = self.clearButton.sizeHint()
frameWidth = self.style().pixelMetric(
QtGui.QStyle.PM_DefaultFrameWidth)
self.clearButton.move(self.rect().right() - frameWidth - sz.width(),
(self.rect().bottom() + 1 - sz.height()) / 2)
if hasattr(self, u'menuButton'):
sz = self.menuButton.sizeHint()
self.menuButton.move(self.rect().left() + frameWidth + 2,
(self.rect().bottom() + 1 - sz.height()) / 2)
def currentSearchType(self):
"""
Readonly property to return the current search type.
"""
return self._currentSearchType
def setSearchTypes(self, items):
"""
A list of tuples to be used in the search type menu. The first item in
the list will be preselected as the default.
``items``
The list of tuples to use. The tuples should contain an integer
identifier, an icon (QIcon instance or string) and a title for the
item in the menu. In short, they should look like this::
(<identifier>, <icon>, <title>)
For instance::
(1, <QIcon instance>, "Titles")
Or::
(2, ":/songs/authors.png", "Authors")
"""
menu = QtGui.QMenu(self)
first = None
for identifier, icon, title in items:
action = QtGui.QAction(build_icon(icon), title, menu)
action.setData(QtCore.QVariant(identifier))
menu.addAction(action)
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'),
self._onMenuActionTriggered)
if first is None:
first = action
self._currentSearchType = identifier
if not hasattr(self, u'menuButton'):
self.menuButton = QtGui.QToolButton(self)
self.menuButton.setIcon(build_icon(u':/system/clear_shortcut.png'))
self.menuButton.setCursor(QtCore.Qt.ArrowCursor)
self.menuButton.setPopupMode(QtGui.QToolButton.InstantPopup)
self.menuButton.setStyleSheet(
u'QToolButton { border: none; padding: 0px 10px 0px 0px; }')
self.menuButton.resize(QtCore.QSize(28, 18))
self.menuButton.setMenu(menu)
self.menuButton.setDefaultAction(first)
self.menuButton.show()
self._updateStyleSheet()
def _onSearchEditTextChanged(self, text):
"""
Internally implemented slot to react to when the text in the line edit
has changed so that we can show or hide the clear button.
``text``
A :class:`~PyQt4.QtCore.QString` instance which represents the text
in the line edit.
"""
self.clearButton.setVisible(not text.isEmpty())
def _onClearButtonClicked(self):
"""
Internally implemented slot to react to the clear button being pressed
to clear the line edit. Once it has cleared the line edit, it emits the
``cleared()`` signal so that an application can react to the clearing
of the line edit.
"""
self.clear()
self.emit(QtCore.SIGNAL(u'cleared()'))
def _onMenuActionTriggered(self):
"""
Internally implemented slot to react to the select of one of the search
types in the menu. Once it has set the correct action on the button,
and set the current search type (using the list of identifiers provided
by the developer), the ``searchTypeChanged(int)`` signal is emitted
with the identifier.
"""
sender = self.sender()
for action in self.menuButton.menu().actions():
action.setChecked(False)
self.menuButton.setDefaultAction(sender)
self._currentSearchType = sender.data().toInt()[0]
self.emit(QtCore.SIGNAL(u'searchTypeChanged(int)'),
self._currentSearchType)

View File

@ -29,6 +29,7 @@ import locale
import re import re
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_
from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
ItemCapabilities, translate, check_item_selected ItemCapabilities, translate, check_item_selected
@ -36,6 +37,7 @@ from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm SongImportForm
from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser
from openlp.plugins.songs.lib.db import Author, Song from openlp.plugins.songs.lib.db import Author, Song
from openlp.core.lib.searchedit import SearchEdit
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -88,20 +90,10 @@ class SongMediaItem(MediaManagerItem):
self.SearchTextLabel.setObjectName(u'SearchTextLabel') self.SearchTextLabel.setObjectName(u'SearchTextLabel')
self.SearchLayout.setWidget( self.SearchLayout.setWidget(
0, QtGui.QFormLayout.LabelRole, self.SearchTextLabel) 0, QtGui.QFormLayout.LabelRole, self.SearchTextLabel)
self.SearchTextEdit = QtGui.QLineEdit(self) self.SearchTextEdit = SearchEdit(self)
self.SearchTextEdit.setObjectName(u'SearchTextEdit') self.SearchTextEdit.setObjectName(u'SearchTextEdit')
self.SearchLayout.setWidget( self.SearchLayout.setWidget(
0, QtGui.QFormLayout.FieldRole, self.SearchTextEdit) 0, QtGui.QFormLayout.FieldRole, self.SearchTextEdit)
self.SearchTypeLabel = QtGui.QLabel(self)
self.SearchTypeLabel.setAlignment(
QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft)
self.SearchTypeLabel.setObjectName(u'SearchTypeLabel')
self.SearchLayout.setWidget(
1, QtGui.QFormLayout.LabelRole, self.SearchTypeLabel)
self.SearchTypeComboBox = QtGui.QComboBox(self)
self.SearchTypeComboBox.setObjectName(u'SearchTypeComboBox')
self.SearchLayout.setWidget(
1, QtGui.QFormLayout.FieldRole, self.SearchTypeComboBox)
self.pageLayout.addLayout(self.SearchLayout) self.pageLayout.addLayout(self.SearchLayout)
self.SearchButtonLayout = QtGui.QHBoxLayout() self.SearchButtonLayout = QtGui.QHBoxLayout()
self.SearchButtonLayout.setMargin(0) self.SearchButtonLayout.setMargin(0)
@ -113,9 +105,6 @@ class SongMediaItem(MediaManagerItem):
self.SearchTextButton = QtGui.QPushButton(self) self.SearchTextButton = QtGui.QPushButton(self)
self.SearchTextButton.setObjectName(u'SearchTextButton') self.SearchTextButton.setObjectName(u'SearchTextButton')
self.SearchButtonLayout.addWidget(self.SearchTextButton) self.SearchButtonLayout.addWidget(self.SearchTextButton)
self.ClearTextButton = QtGui.QPushButton(self)
self.ClearTextButton.setObjectName(u'ClearTextButton')
self.SearchButtonLayout.addWidget(self.ClearTextButton)
self.pageLayout.addLayout(self.SearchButtonLayout) self.pageLayout.addLayout(self.SearchButtonLayout)
# Signals and slots # Signals and slots
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
@ -124,8 +113,6 @@ class SongMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick) QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick)
QtCore.QObject.connect(self.SearchTextButton, QtCore.QObject.connect(self.SearchTextButton,
QtCore.SIGNAL(u'pressed()'), self.onSearchTextButtonClick) QtCore.SIGNAL(u'pressed()'), self.onSearchTextButtonClick)
QtCore.QObject.connect(self.ClearTextButton,
QtCore.SIGNAL(u'pressed()'), self.onClearTextButtonClick)
QtCore.QObject.connect(self.SearchTextEdit, QtCore.QObject.connect(self.SearchTextEdit,
QtCore.SIGNAL(u'textChanged(const QString&)'), QtCore.SIGNAL(u'textChanged(const QString&)'),
self.onSearchTextEditChanged) self.onSearchTextEditChanged)
@ -139,6 +126,11 @@ class SongMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'songs_edit'), self.onRemoteEdit) QtCore.SIGNAL(u'songs_edit'), self.onRemoteEdit)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_edit_clear'), self.onRemoteEditClear) QtCore.SIGNAL(u'songs_edit_clear'), self.onRemoteEditClear)
QtCore.QObject.connect(self.SearchTextEdit,
QtCore.SIGNAL(u'cleared()'), self.onClearTextButtonClick)
QtCore.QObject.connect(self.SearchTextEdit,
QtCore.SIGNAL(u'searchTypeChanged(int)'),
self.onSearchTextButtonClick)
def configUpdated(self): def configUpdated(self):
self.searchAsYouType = QtCore.QSettings().value( self.searchAsYouType = QtCore.QSettings().value(
@ -154,39 +146,44 @@ class SongMediaItem(MediaManagerItem):
def retranslateUi(self): def retranslateUi(self):
self.SearchTextLabel.setText( self.SearchTextLabel.setText(
translate('SongsPlugin.MediaItem', 'Search:')) translate('SongsPlugin.MediaItem', 'Search:'))
self.SearchTypeLabel.setText(
translate('SongsPlugin.MediaItem', 'Type:'))
self.ClearTextButton.setText(
translate('SongsPlugin.MediaItem', 'Clear'))
self.SearchTextButton.setText( self.SearchTextButton.setText(
translate('SongsPlugin.MediaItem', 'Search')) translate('SongsPlugin.MediaItem', 'Search'))
def initialise(self): def initialise(self):
self.SearchTypeComboBox.addItem( self.SearchTextEdit.setSearchTypes([
translate('SongsPlugin.MediaItem', 'Titles')) (1, u':/songs/song_search_all.png', translate('SongsPlugin.MediaItem', 'Entire Song')),
self.SearchTypeComboBox.addItem( (2, u':/songs/song_search_title.png', translate('SongsPlugin.MediaItem', 'Titles')),
translate('SongsPlugin.MediaItem', 'Lyrics')) (3, u':/songs/song_search_lyrics.png', translate('SongsPlugin.MediaItem', 'Lyrics')),
self.SearchTypeComboBox.addItem( (4, u':/songs/song_search_author.png', translate('SongsPlugin.MediaItem', 'Authors'))
translate('SongsPlugin.MediaItem', 'Authors')) ])
self.configUpdated() self.configUpdated()
def onSearchTextButtonClick(self): def onSearchTextButtonClick(self):
search_keywords = unicode(self.SearchTextEdit.displayText()) search_keywords = unicode(self.SearchTextEdit.displayText())
search_results = [] search_results = []
search_type = self.SearchTypeComboBox.currentIndex() # search_type = self.SearchTypeComboBox.currentIndex()
if search_type == 0: search_type = self.SearchTextEdit.currentSearchType()
if search_type == 1:
log.debug(u'Entire Song Search')
search_results = self.parent.manager.get_all_objects(Song,
or_(Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'),
Song.search_lyrics.like(u'%' + search_keywords.lower() + \
u'%')), Song.search_title.asc())
self.displayResultsSong(search_results)
if search_type == 2:
log.debug(u'Titles Search') log.debug(u'Titles Search')
search_results = self.parent.manager.get_all_objects(Song, search_results = self.parent.manager.get_all_objects(Song,
Song.search_title.like(u'%' + self.whitespace.sub(u' ', Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'), Song.search_title.asc()) search_keywords.lower()) + u'%'), Song.search_title.asc())
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
elif search_type == 1: elif search_type == 3:
log.debug(u'Lyrics Search') log.debug(u'Lyrics Search')
search_results = self.parent.manager.get_all_objects(Song, search_results = self.parent.manager.get_all_objects(Song,
Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'), Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'),
Song.search_lyrics.asc()) Song.search_lyrics.asc())
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
elif search_type == 2: elif search_type == 4:
log.debug(u'Authors Search') log.debug(u'Authors Search')
search_results = self.parent.manager.get_all_objects(Author, search_results = self.parent.manager.get_all_objects(Author,
Author.display_name.like(u'%' + search_keywords + u'%'), Author.display_name.like(u'%' + search_keywords + u'%'),
@ -255,7 +252,9 @@ class SongMediaItem(MediaManagerItem):
""" """
if self.searchAsYouType: if self.searchAsYouType:
search_length = 1 search_length = 1
if self.SearchTypeComboBox.currentIndex() == 1: if self.SearchTextEdit.currentSearchType() == 1:
search_length = 3
elif self.SearchTextEdit.currentSearchType() == 3:
search_length = 7 search_length = 7
if len(text) > search_length: if len(text) > search_length:
self.onSearchTextButtonClick() self.onSearchTextButtonClick()

Binary file not shown.

After

Width:  |  Height:  |  Size: 644 B

View File

@ -1,5 +1,9 @@
<RCC> <RCC>
<qresource prefix="songs"> <qresource prefix="songs">
<file>song_search_all.png</file>
<file>song_search_author.png</file>
<file>song_search_lyrics.png</file>
<file>song_search_title.png</file>
<file>topic_edit.png</file> <file>topic_edit.png</file>
<file>author_add.png</file> <file>author_add.png</file>
<file>author_delete.png</file> <file>author_delete.png</file>

Binary file not shown.

After

Width:  |  Height:  |  Size: 607 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B