Cleanup creation of menus for toolbar buttons and add the keyboard shortcut G for Get Books

This commit is contained in:
Kovid Goyal 2011-08-02 14:46:46 -06:00
parent a5e8b23bfe
commit f929fe73df
13 changed files with 80 additions and 61 deletions

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial from functools import partial
from zipfile import ZipFile from zipfile import ZipFile
from PyQt4.Qt import QToolButton, QAction, QIcon, QObject from PyQt4.Qt import QToolButton, QAction, QIcon, QObject, QMenu
from calibre.gui2 import Dispatcher from calibre.gui2 import Dispatcher
@ -66,6 +66,14 @@ class InterfaceAction(QObject):
#: shortcut must be a translated string if not None #: shortcut must be a translated string if not None
action_spec = ('text', 'icon', None, None) action_spec = ('text', 'icon', None, None)
#: If True, a menu is automatically created and added to self.qaction
action_add_menu = False
#: If True, a clone of self.qaction is added to the menu of self.qaction
#: If you want the text of this action to be different from that of
#: self.qaction, set this variable to the new text
action_menu_clone_qaction = False
#: Set of locations to which this action must not be added. #: Set of locations to which this action must not be added.
#: See :attr:`all_locations` for a list of possible locations #: See :attr:`all_locations` for a list of possible locations
dont_add_to = frozenset([]) dont_add_to = frozenset([])
@ -94,6 +102,7 @@ class InterfaceAction(QObject):
self.Dispatcher = partial(Dispatcher, parent=self) self.Dispatcher = partial(Dispatcher, parent=self)
self.create_action() self.create_action()
self.gui.addAction(self.qaction) self.gui.addAction(self.qaction)
self.gui.addAction(self.menuless_qaction)
self.genesis() self.genesis()
def create_action(self, spec=None, attr='qaction'): def create_action(self, spec=None, attr='qaction'):
@ -104,18 +113,29 @@ class InterfaceAction(QObject):
action = QAction(QIcon(I(icon)), text, self.gui) action = QAction(QIcon(I(icon)), text, self.gui)
else: else:
action = QAction(text, self.gui) action = QAction(text, self.gui)
action.setAutoRepeat(self.auto_repeat) if attr == 'qaction':
text = tooltip if tooltip else text mt = (action.text() if self.action_menu_clone_qaction is True else
action.setToolTip(text) unicode(self.action_menu_clone_qaction))
action.setStatusTip(text) self.menuless_qaction = ma = QAction(action.icon(), mt, self.gui)
action.setWhatsThis(text) ma.triggered.connect(action.trigger)
action.setAutoRepeat(False) for a in ((action, ma) if attr == 'qaction' else (action,)):
a.setAutoRepeat(self.auto_repeat)
text = tooltip if tooltip else text
a.setToolTip(text)
a.setStatusTip(text)
a.setWhatsThis(text)
if shortcut: if shortcut:
a = ma if attr == 'qaction' else action
if isinstance(shortcut, list): if isinstance(shortcut, list):
action.setShortcuts(shortcut) a.setShortcuts(shortcut)
else: else:
action.setShortcut(shortcut) a.setShortcut(shortcut)
setattr(self, attr, action) setattr(self, attr, action)
if attr == 'qaction' and self.action_add_menu:
menu = QMenu()
action.setMenu(menu)
if self.action_menu_clone_qaction:
menu.addAction(self.menuless_qaction)
return action return action
def load_resources(self, names): def load_resources(self, names):

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
import os import os
from functools import partial from functools import partial
from PyQt4.Qt import QPixmap, QMenu, QTimer from PyQt4.Qt import QPixmap, QTimer
from calibre.gui2 import error_dialog, choose_files, \ from calibre.gui2 import error_dialog, choose_files, \
@ -48,12 +48,12 @@ class AddAction(InterfaceAction):
_('Add books to the calibre library/device from files on your computer') _('Add books to the calibre library/device from files on your computer')
, _('A')) , _('A'))
action_type = 'current' action_type = 'current'
action_add_menu = True
action_menu_clone_qaction = _('Add books from a single directory')
def genesis(self): def genesis(self):
self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book) self._add_filesystem_book = self.Dispatcher(self.__add_filesystem_book)
self.add_menu = QMenu() self.add_menu = self.qaction.menu()
self.add_menu.addAction(_('Add books from a single directory'),
self.add_books)
self.add_menu.addAction(_('Add books from directories, including ' self.add_menu.addAction(_('Add books from directories, including '
'sub-directories (One book per directory, assumes every ebook ' 'sub-directories (One book per directory, assumes every ebook '
'file is the same book in a different format)'), 'file is the same book in a different format)'),
@ -69,7 +69,6 @@ class AddAction(InterfaceAction):
self.add_menu.addAction(_('Add files to selected book records'), self.add_menu.addAction(_('Add files to selected book records'),
self.add_formats, _('Shift+A')) self.add_formats, _('Shift+A'))
self.qaction.setMenu(self.add_menu)
self.qaction.triggered.connect(self.add_books) self.qaction.triggered.connect(self.add_books)
def location_selected(self, loc): def location_selected(self, loc):

View File

@ -82,23 +82,20 @@ class ChooseLibraryAction(InterfaceAction):
action_spec = (_('%d books'), 'lt.png', action_spec = (_('%d books'), 'lt.png',
_('Choose calibre library to work with'), None) _('Choose calibre library to work with'), None)
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
action_add_menu = True
action_menu_clone_qaction = _('Switch/create library...')
def genesis(self): def genesis(self):
self.count_changed(0) self.count_changed(0)
self.qaction.triggered.connect(self.choose_library, self.qaction.triggered.connect(self.choose_library,
type=Qt.QueuedConnection) type=Qt.QueuedConnection)
self.action_choose = self.menuless_qaction
self.stats = LibraryUsageStats() self.stats = LibraryUsageStats()
self.popup_type = (QToolButton.InstantPopup if len(self.stats.stats) > 1 else self.popup_type = (QToolButton.InstantPopup if len(self.stats.stats) > 1 else
QToolButton.MenuButtonPopup) QToolButton.MenuButtonPopup)
self.create_action(spec=(_('Switch/create library...'), 'lt.png', None, self.choose_menu = self.qaction.menu()
None), attr='action_choose')
self.action_choose.triggered.connect(self.choose_library,
type=Qt.QueuedConnection)
self.choose_menu = QMenu(self.gui)
self.qaction.setMenu(self.choose_menu)
if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None): if not os.environ.get('CALIBRE_OVERRIDE_DATABASE_PATH', None):
self.choose_menu.addAction(self.action_choose) self.choose_menu.addAction(self.action_choose)
@ -110,7 +107,7 @@ class ChooseLibraryAction(InterfaceAction):
self.delete_menu = QMenu(_('Remove library')) self.delete_menu = QMenu(_('Remove library'))
self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu) self.delete_menu_action = self.choose_menu.addMenu(self.delete_menu)
ac = self.create_action(spec=(_('Pick a random book'), 'catalog.png', ac = self.create_action(spec=(_('Pick a random book'), 'random.png',
None, None), attr='action_pick_random') None, None), attr='action_pick_random')
ac.triggered.connect(self.pick_random) ac.triggered.connect(self.pick_random)
self.choose_menu.addAction(ac) self.choose_menu.addAction(ac)

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
import os import os
from functools import partial from functools import partial
from PyQt4.Qt import QModelIndex, QMenu from PyQt4.Qt import QModelIndex, QIcon
from calibre.gui2 import error_dialog, Dispatcher from calibre.gui2 import error_dialog, Dispatcher
from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook from calibre.gui2.tools import convert_single_ebook, convert_bulk_ebook
@ -22,18 +22,18 @@ class ConvertAction(InterfaceAction):
action_spec = (_('Convert books'), 'convert.png', None, _('C')) action_spec = (_('Convert books'), 'convert.png', None, _('C'))
dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
action_type = 'current' action_type = 'current'
action_add_menu = True
def genesis(self): def genesis(self):
cm = QMenu() cm = self.qaction.menu()
cm.addAction(_('Convert individually'), partial(self.convert_ebook, cm.addAction(self.qaction.icon(), _('Convert individually'), partial(self.convert_ebook,
False, bulk=False)) False, bulk=False))
cm.addAction(_('Bulk convert'), cm.addAction(_('Bulk convert'),
partial(self.convert_ebook, False, bulk=True)) partial(self.convert_ebook, False, bulk=True))
cm.addSeparator() cm.addSeparator()
ac = cm.addAction( ac = cm.addAction(QIcon(I('catalog.png')),
_('Create a catalog of the books in your calibre library')) _('Create a catalog of the books in your calibre library'))
ac.triggered.connect(self.gui.iactions['Generate Catalog'].generate_catalog) ac.triggered.connect(self.gui.iactions['Generate Catalog'].generate_catalog)
self.qaction.setMenu(cm)
self.qaction.triggered.connect(self.convert_ebook) self.qaction.triggered.connect(self.convert_ebook)
self.convert_menu = cm self.convert_menu = cm
self.conversion_jobs = {} self.conversion_jobs = {}

View File

@ -9,7 +9,7 @@ import os
from functools import partial from functools import partial
from threading import Thread from threading import Thread
from PyQt4.Qt import QMenu, QToolButton from PyQt4.Qt import QToolButton
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
from calibre.gui2 import error_dialog, Dispatcher, warning_dialog from calibre.gui2 import error_dialog, Dispatcher, warning_dialog
@ -95,10 +95,10 @@ class CopyToLibraryAction(InterfaceAction):
popup_type = QToolButton.InstantPopup popup_type = QToolButton.InstantPopup
dont_add_to = frozenset(['toolbar-device', 'context-menu-device']) dont_add_to = frozenset(['toolbar-device', 'context-menu-device'])
action_type = 'current' action_type = 'current'
action_add_menu = True
def genesis(self): def genesis(self):
self.menu = QMenu(self.gui) self.menu = self.qaction.menu()
self.qaction.setMenu(self.menu)
@property @property
def stats(self): def stats(self):

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial from functools import partial
from PyQt4.Qt import QMenu, QObject, QTimer from PyQt4.Qt import QObject, QTimer
from calibre.gui2 import error_dialog, question_dialog from calibre.gui2 import error_dialog, question_dialog
from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog from calibre.gui2.dialogs.delete_matching_from_device import DeleteMatchingFromDeviceDialog
@ -18,7 +18,7 @@ from calibre.utils.recycle_bin import can_recycle
single_shot = partial(QTimer.singleShot, 10) single_shot = partial(QTimer.singleShot, 10)
class MultiDeleter(QObject): class MultiDeleter(QObject): # {{{
def __init__(self, gui, ids, callback): def __init__(self, gui, ids, callback):
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
@ -77,17 +77,19 @@ class MultiDeleter(QObject):
error_dialog(self.gui, _('Failed to delete'), error_dialog(self.gui, _('Failed to delete'),
_('Failed to delete some books, click the Show Details button' _('Failed to delete some books, click the Show Details button'
' for details.'), det_msg='\n\n'.join(msg), show=True) ' for details.'), det_msg='\n\n'.join(msg), show=True)
# }}}
class DeleteAction(InterfaceAction): class DeleteAction(InterfaceAction):
name = 'Remove Books' name = 'Remove Books'
action_spec = (_('Remove books'), 'trash.png', None, 'Del') action_spec = (_('Remove books'), 'trash.png', None, 'Del')
action_type = 'current' action_type = 'current'
action_add_menu = True
action_menu_clone_qaction = _('Remove selected books')
def genesis(self): def genesis(self):
self.qaction.triggered.connect(self.delete_books) self.qaction.triggered.connect(self.delete_books)
self.delete_menu = QMenu() self.delete_menu = self.qaction.menu()
self.delete_menu.addAction(_('Remove selected books'), self.delete_books)
self.delete_menu.addAction( self.delete_menu.addAction(
_('Remove files of a specific format from selected books..'), _('Remove files of a specific format from selected books..'),
self.delete_selected_formats) self.delete_selected_formats)

View File

@ -24,12 +24,13 @@ class EditMetadataAction(InterfaceAction):
name = 'Edit Metadata' name = 'Edit Metadata'
action_spec = (_('Edit metadata'), 'edit_input.png', None, _('E')) action_spec = (_('Edit metadata'), 'edit_input.png', None, _('E'))
action_type = 'current' action_type = 'current'
action_add_menu = True
def genesis(self): def genesis(self):
self.create_action(spec=(_('Merge book records'), 'merge_books.png', self.create_action(spec=(_('Merge book records'), 'merge_books.png',
None, _('M')), attr='action_merge') None, _('M')), attr='action_merge')
md = QMenu() md = self.qaction.menu()
md.addAction(_('Edit metadata individually'), md.addAction(self.qaction.icon(), _('Edit metadata individually'),
partial(self.edit_metadata, False, bulk=False)) partial(self.edit_metadata, False, bulk=False))
md.addSeparator() md.addSeparator()
md.addAction(_('Edit metadata in bulk'), md.addAction(_('Edit metadata in bulk'),
@ -56,7 +57,6 @@ class EditMetadataAction(InterfaceAction):
md.addAction(self.action_merge) md.addAction(self.action_merge)
self.qaction.triggered.connect(self.edit_metadata) self.qaction.triggered.connect(self.edit_metadata)
self.qaction.setMenu(md)
self.action_merge.triggered.connect(self.merge_books) self.action_merge.triggered.connect(self.merge_books)
def location_selected(self, loc): def location_selected(self, loc):

View File

@ -5,7 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import QIcon, QMenu, Qt from PyQt4.Qt import QIcon, Qt
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
from calibre.gui2.preferences.main import Preferences from calibre.gui2.preferences.main import Preferences
@ -16,12 +16,13 @@ class PreferencesAction(InterfaceAction):
name = 'Preferences' name = 'Preferences'
action_spec = (_('Preferences'), 'config.png', None, _('Ctrl+P')) action_spec = (_('Preferences'), 'config.png', None, _('Ctrl+P'))
action_add_menu = True
action_menu_clone_qaction = _('Change calibre behavior')
def genesis(self): def genesis(self):
pm = QMenu() pm = self.qaction.menu()
pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config)
if isosx: if isosx:
pm.addAction(QIcon(I('config.png')), _('Change calibre behavior'), self.do_config) pm.addAction(QIcon(I('config.png')), _('Preferences'), self.do_config)
pm.addAction(QIcon(I('wizard.png')), _('Run welcome wizard'), pm.addAction(QIcon(I('wizard.png')), _('Run welcome wizard'),
self.gui.run_wizard) self.gui.run_wizard)
pm.addAction(QIcon(I('plugins/plugin_updater.png')), pm.addAction(QIcon(I('plugins/plugin_updater.png')),
@ -33,7 +34,6 @@ class PreferencesAction(InterfaceAction):
ac.setShortcut('Ctrl+Shift+R') ac.setShortcut('Ctrl+Shift+R')
self.gui.addAction(ac) self.gui.addAction(ac)
self.qaction.setMenu(pm)
self.preferences_menu = pm self.preferences_menu = pm
for x in (self.gui.preferences_action, self.qaction): for x in (self.gui.preferences_action, self.qaction):
x.triggered.connect(self.do_config) x.triggered.connect(self.do_config)

View File

@ -38,12 +38,12 @@ class SaveToDiskAction(InterfaceAction):
name = "Save To Disk" name = "Save To Disk"
action_spec = (_('Save to disk'), 'save.png', None, _('S')) action_spec = (_('Save to disk'), 'save.png', None, _('S'))
action_type = 'current' action_type = 'current'
action_add_menu = True
action_menu_clone_qaction = True
def genesis(self): def genesis(self):
self.qaction.triggered.connect(self.save_to_disk) self.qaction.triggered.connect(self.save_to_disk)
self.save_menu = QMenu() self.save_menu = self.qaction.menu()
self.save_menu.addAction(_('Save to disk'), partial(self.save_to_disk,
False))
self.save_menu.addAction(_('Save to disk in a single directory'), self.save_menu.addAction(_('Save to disk in a single directory'),
partial(self.save_to_single_dir, False)) partial(self.save_to_single_dir, False))
self.save_menu.addAction(_('Save only %s format to disk')% self.save_menu.addAction(_('Save only %s format to disk')%
@ -56,7 +56,6 @@ class SaveToDiskAction(InterfaceAction):
self.save_sub_menu = SaveMenu(self.gui) self.save_sub_menu = SaveMenu(self.gui)
self.save_sub_menu_action = self.save_menu.addMenu(self.save_sub_menu) self.save_sub_menu_action = self.save_menu.addMenu(self.save_sub_menu)
self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk) self.save_sub_menu.save_fmt.connect(self.save_specific_format_disk)
self.qaction.setMenu(self.save_menu)
def location_selected(self, loc): def location_selected(self, loc):
enabled = loc == 'library' enabled = loc == 'library'

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial from functools import partial
from PyQt4.Qt import QMenu, QToolButton from PyQt4.Qt import QToolButton
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
@ -17,9 +17,10 @@ class SimilarBooksAction(InterfaceAction):
action_spec = (_('Similar books...'), None, None, None) action_spec = (_('Similar books...'), None, None, None)
popup_type = QToolButton.InstantPopup popup_type = QToolButton.InstantPopup
action_type = 'current' action_type = 'current'
action_add_menu = True
def genesis(self): def genesis(self):
m = QMenu(self.gui) m = self.qaction.menu()
for text, icon, target, shortcut in [ for text, icon, target, shortcut in [
(_('Books by same author'), 'user_profile.png', 'authors', _('Alt+A')), (_('Books by same author'), 'user_profile.png', 'authors', _('Alt+A')),
(_('Books in this series'), 'books_in_series.png', 'series', (_('Books in this series'), 'books_in_series.png', 'series',
@ -31,7 +32,6 @@ class SimilarBooksAction(InterfaceAction):
m.addAction(ac) m.addAction(ac)
ac.triggered.connect(partial(self.show_similar_books, target)) ac.triggered.connect(partial(self.show_similar_books, target))
self.qaction.setMenu(m) self.qaction.setMenu(m)
self.similar_menu = m
def show_similar_books(self, type, *args): def show_similar_books(self, type, *args):
search, join = [], ' ' search, join = [], ' '

View File

@ -8,7 +8,7 @@ __docformat__ = 'restructuredtext en'
from functools import partial from functools import partial
from PyQt4.Qt import QMenu, QIcon, QSize from PyQt4.Qt import QIcon, QSize
from calibre.gui2 import error_dialog from calibre.gui2 import error_dialog
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
@ -17,16 +17,18 @@ from calibre.gui2.dialogs.confirm_delete import confirm
class StoreAction(InterfaceAction): class StoreAction(InterfaceAction):
name = 'Store' name = 'Store'
action_spec = (_('Get books'), 'store.png', None, None) action_spec = (_('Get books'), 'store.png', None, _('G'))
action_add_menu = True
action_menu_clone_qaction = _('Search for ebooks')
def genesis(self): def genesis(self):
self.qaction.triggered.connect(self.do_search) self.qaction.triggered.connect(self.do_search)
self.store_menu = QMenu() self.store_menu = self.qaction.menu()
self.load_menu() self.load_menu()
def load_menu(self): def load_menu(self):
self.store_menu.clear() self.store_menu.clear()
self.store_menu.addAction(_('Search for ebooks'), self.search) self.store_menu.addAction(self.menuless_qaction)
self.store_menu.addAction(_('Search for this author'), self.search_author) self.store_menu.addAction(_('Search for this author'), self.search_author)
self.store_menu.addAction(_('Search for this title'), self.search_title) self.store_menu.addAction(_('Search for this title'), self.search_title)
self.store_menu.addAction(_('Search for this book'), self.search_author_title) self.store_menu.addAction(_('Search for this book'), self.search_author_title)
@ -41,7 +43,6 @@ class StoreAction(InterfaceAction):
self.store_list_menu.addAction(n, partial(self.open_store, p)) self.store_list_menu.addAction(n, partial(self.open_store, p))
self.store_menu.addSeparator() self.store_menu.addSeparator()
self.store_menu.addAction(_('Choose stores'), self.choose) self.store_menu.addAction(_('Choose stores'), self.choose)
self.qaction.setMenu(self.store_menu)
def do_search(self): def do_search(self):
return self.search() return self.search()

View File

@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en'
import os, time import os, time
from PyQt4.Qt import Qt, QMenu, QAction, pyqtSignal from PyQt4.Qt import Qt, QAction, pyqtSignal
from calibre.constants import isosx from calibre.constants import isosx
from calibre.gui2 import error_dialog, Dispatcher, question_dialog, config, \ from calibre.gui2 import error_dialog, Dispatcher, question_dialog, config, \
@ -35,20 +35,19 @@ class ViewAction(InterfaceAction):
name = 'View' name = 'View'
action_spec = (_('View'), 'view.png', None, _('V')) action_spec = (_('View'), 'view.png', None, _('V'))
action_type = 'current' action_type = 'current'
action_add_menu = True
action_menu_clone_qaction = True
def genesis(self): def genesis(self):
self.persistent_files = [] self.persistent_files = []
self.qaction.triggered.connect(self.view_book) self.qaction.triggered.connect(self.view_book)
self.view_menu = QMenu() self.view_action = self.menuless_qaction
self.view_menu = self.qaction.menu()
ac = self.view_specific_action = QAction(_('View specific format'), ac = self.view_specific_action = QAction(_('View specific format'),
self.gui) self.gui)
self.qaction.setMenu(self.view_menu)
ac.setShortcut(Qt.AltModifier+Qt.Key_V) ac.setShortcut(Qt.AltModifier+Qt.Key_V)
ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection) ac.triggered.connect(self.view_specific_format, type=Qt.QueuedConnection)
ac = self.view_action = QAction(self.qaction.icon(), ac = self.create_action(spec=(_('Read a random book'), 'random.png',
self.qaction.text(), self.gui)
ac.triggered.connect(self.view_book)
ac = self.create_action(spec=(_('Read a random book'), 'catalog.png',
None, None), attr='action_pick_random') None, None), attr='action_pick_random')
ac.triggered.connect(self.view_random) ac.triggered.connect(self.view_random)
ac = self.clear_history_action = QAction( ac = self.clear_history_action = QAction(

View File

@ -527,6 +527,8 @@ Calibre has several keyboard shortcuts to save you time and mouse movement. Thes
- Remove selected Books - Remove selected Books
* - :kbd:`E` * - :kbd:`E`
- Edit metadata of selected books - Edit metadata of selected books
* - :kbd:`G`
- Get Books
* - :kbd:`I` * - :kbd:`I`
- Show book details - Show book details
* - :kbd:`M` * - :kbd:`M`