From 1718859647491e8ea226cb501d4da003f4e62358 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 3 May 2010 20:00:39 -0600 Subject: [PATCH] Add a sidebar to the main GUI to control the optional views (tag browser, book info and cover browser) --- src/calibre/gui2/main.ui | 528 +++++++++++++++++++----------------- src/calibre/gui2/sidebar.py | 235 ++++++++++++++++ src/calibre/gui2/status.py | 149 +++------- src/calibre/gui2/ui.py | 94 ++----- src/calibre/gui2/widgets.py | 25 +- 5 files changed, 589 insertions(+), 442 deletions(-) create mode 100644 src/calibre/gui2/sidebar.py diff --git a/src/calibre/gui2/main.ui b/src/calibre/gui2/main.ui index 68f2b8b6ba..8dcb0e6d75 100644 --- a/src/calibre/gui2/main.ui +++ b/src/calibre/gui2/main.ui @@ -150,7 +150,7 @@ - + 6 @@ -288,271 +288,285 @@ - - - - 0 - 100 - - - - Qt::Vertical - - - - - 100 - 100 - - - - 0 - - - - - - - Qt::Horizontal - - - - - - - true - - - true - - - true - - - true - - - - - - - Sort by &popularity - - - - - + + + + + + 0 + 100 + + + + Qt::Vertical + + + + + 100 + 100 + + + + 0 + + + + + + + Qt::Horizontal + + + - - - 0 + + + true + + true + + + true + + + true + + + + + + + Sort by &popularity + + + + + - - Match any - + + + 0 + + + + Match any + + + + + Match all + + + - - Match all - + + + Create, edit, and delete user categories + + + Manage &user categories + + - + - - - Create, edit, and delete user categories - - - Manage &user categories - - + + + + + &Restrict to: + + + search_restriction + + + + + + + + 50 + 0 + + + + Books display will be restricted to those matching the selected saved search + + + + - - - - - - - &Restrict to: - - - search_restriction - - - - - - - - 50 - 0 - - - - Books display will be restricted to those matching the selected saved search - - - - - - - - - - - 100 - 10 - - - - true - - - true - - - false - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::SelectRows - - - false - - - false - - - - - + + + + + 100 + 10 + + + + true + + + true + + + false + + + QAbstractItemView::DragDrop + + + true + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + + + + + + + 100 + 10 + + + + true + + + true + + + false + + + QAbstractItemView::DragDrop + + + true + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + + + + + + 10 + 10 + + + + true + + + true + + + false + + + QAbstractItemView::DragDrop + + + true + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + + + + + + 10 + 10 + + + + true + + + true + + + false + + + QAbstractItemView::DragDrop + + + true + + + QAbstractItemView::SelectRows + + + false + + + false + + + + + + + - - - - - - - 100 - 10 - - - - true - - - true - - - false - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::SelectRows - - - false - - - false - - - - + + + + + + 0 + 0 + + - - - - - - - 10 - 10 - - - - true - - - true - - - false - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::SelectRows - - - false - - - false - - - - - - - - - - - - 10 - 10 - - - - true - - - true - - - false - - - QAbstractItemView::DragDrop - - - true - - - QAbstractItemView::SelectRows - - - false - - - false - - - - - - - - + + @@ -832,6 +846,12 @@
calibre/gui2/widgets.h
1 + + SideBar + QWidget +
calibre/gui2/sidebar.h
+ 1 +
diff --git a/src/calibre/gui2/sidebar.py b/src/calibre/gui2/sidebar.py new file mode 100644 index 0000000000..375aafbaa2 --- /dev/null +++ b/src/calibre/gui2/sidebar.py @@ -0,0 +1,235 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +__license__ = 'GPL v3' +__copyright__ = '2010, Kovid Goyal ' +__docformat__ = 'restructuredtext en' + +import re +from functools import partial + +from PyQt4.Qt import QToolBar, Qt, QIcon, QSizePolicy, QWidget, \ + QFrame, QVBoxLayout, QLabel, QSize, QCoreApplication, QToolButton + +from calibre.gui2.progress_indicator import ProgressIndicator +from calibre.gui2 import dynamic + +class JobsButton(QFrame): + + def __init__(self, parent): + QFrame.__init__(self, parent) + self.setLayout(QVBoxLayout()) + self.pi = ProgressIndicator(self) + self.layout().addWidget(self.pi) + self.jobs = QLabel(''+_('Jobs:')+' 0') + self.jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom) + self.layout().addWidget(self.jobs) + self.layout().setAlignment(self.jobs, Qt.AlignHCenter) + self.jobs.setMargin(0) + self.layout().setMargin(0) + self.jobs.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) + self.setCursor(Qt.PointingHandCursor) + self.setToolTip(_('Click to see list of active jobs.')) + + def initialize(self, jobs_dialog): + self.jobs_dialog = jobs_dialog + self.jobs_dialog.jobs_view.restore_column_widths() + + def mouseReleaseEvent(self, event): + if self.jobs_dialog.isVisible(): + self.jobs_dialog.jobs_view.write_settings() + self.jobs_dialog.hide() + else: + self.jobs_dialog.jobs_view.read_settings() + self.jobs_dialog.show() + self.jobs_dialog.jobs_view.restore_column_widths() + + @property + def is_running(self): + return self.pi.isAnimated() + + def start(self): + self.pi.startAnimation() + + def stop(self): + self.pi.stopAnimation() + + +class Jobs(ProgressIndicator): + + def initialize(self, jobs_dialog): + self.jobs_dialog = jobs_dialog + + def mouseClickEvent(self, event): + if self.jobs_dialog.isVisible(): + self.jobs_dialog.jobs_view.write_settings() + self.jobs_dialog.hide() + else: + self.jobs_dialog.jobs_view.read_settings() + self.jobs_dialog.show() + self.jobs_dialog.jobs_view.restore_column_widths() + + @property + def is_running(self): + return self.isAnimated() + + def start(self): + self.startAnimation() + + def stop(self): + self.stopAnimation() + + + +class SideBar(QToolBar): + + toggle_texts = { + 'book_info' : (_('Show Book Details'), _('Hide Book Details')), + 'tag_browser' : (_('Show Tag Browser'), _('Hide Tag Browser')), + 'cover_browser': (_('Show Cover Browser'), _('Hide Cover Browser')), + } + toggle_icons = { + 'book_info' : 'book.svg', + 'tag_browser' : 'tags.svg', + 'cover_browser': 'cover_flow.svg', + } + + + def __init__(self, parent=None): + QToolBar.__init__(self, _('Side bar'), parent) + self.setOrientation(Qt.Vertical) + self.setMovable(False) + self.setFloatable(False) + self.setToolButtonStyle(Qt.ToolButtonIconOnly) + self.setIconSize(QSize(48, 48)) + + for ac in ('book_info', 'tag_browser', 'cover_browser'): + action = self.addAction(QIcon(I(self.toggle_icons[ac])), + self.toggle_texts[ac][1], getattr(self, '_toggle_'+ac)) + setattr(self, 'action_toggle_'+ac, action) + w = self.widgetForAction(action) + w.setCheckable(True) + setattr(self, 'show_'+ac, partial(getattr(self, '_toggle_'+ac), + show=True)) + setattr(self, 'hide_'+ac, partial(getattr(self, '_toggle_'+ac), + show=False)) + + + self.spacer = QWidget(self) + self.spacer.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) + self.addWidget(self.spacer) + self.jobs_button = JobsButton(self) + self.addWidget(self.jobs_button) + + self.show_cover_browser = partial(self._toggle_cover_browser, show=True) + self.hide_cover_browser = partial(self._toggle_cover_browser, + show=False) + for ch in self.children(): + if isinstance(ch, QToolButton): + ch.setCursor(Qt.PointingHandCursor) + + def initialize(self, jobs_dialog, cover_browser, toggle_cover_browser, + cover_browser_error, vertical_splitter, horizontal_splitter): + self.jobs_button.initialize(jobs_dialog) + self.cover_browser, self.do_toggle_cover_browser = cover_browser, \ + toggle_cover_browser + if self.cover_browser is None: + self.action_toggle_cover_browser.setEnabled(False) + self.action_toggle_cover_browser.setText( + _('Cover browser could not be loaded: ') + cover_browser_error) + else: + self.cover_browser.stop.connect(self.hide_cover_browser) + self._toggle_cover_browser(dynamic.get('cover_flow_visible', False)) + + self.horizontal_splitter = horizontal_splitter + self.vertical_splitter = vertical_splitter + + tb_state = dynamic.get('tag_browser_state', None) + if tb_state is not None: + self.horizontal_splitter.restoreState(tb_state) + + bi_state = dynamic.get('book_info_state', None) + if bi_state is not None: + self.vertical_splitter.restoreState(bi_state) + self.horizontal_splitter.initialize() + self.vertical_splitter.initialize() + self.view_status_changed('book_info', not + self.vertical_splitter.is_side_index_hidden) + self.view_status_changed('tag_browser', not + self.horizontal_splitter.is_side_index_hidden) + self.vertical_splitter.state_changed.connect(partial(self.view_status_changed, + 'book_info'), type=Qt.QueuedConnection) + self.horizontal_splitter.state_changed.connect(partial(self.view_status_changed, + 'tag_browser'), type=Qt.QueuedConnection) + + + + def view_status_changed(self, name, visible): + action = getattr(self, 'action_toggle_'+name) + texts = self.toggle_texts[name] + action.setText(texts[int(visible)]) + w = self.widgetForAction(action) + w.setCheckable(True) + w.setChecked(visible) + + def location_changed(self, location): + is_lib = location == 'library' + for ac in ('cover_browser', 'tag_browser'): + ac = getattr(self, 'action_toggle_'+ac) + ac.setEnabled(is_lib) + self.widgetForAction(ac).setVisible(is_lib) + + def save_state(self): + dynamic.set('cover_flow_visible', self.is_cover_browser_visible) + dynamic.set('tag_browser_state', + str(self.horizontal_splitter.saveState())) + dynamic.set('book_info_state', + str(self.vertical_splitter.saveState())) + + + @property + def is_cover_browser_visible(self): + return self.cover_browser is not None and self.cover_browser.isVisible() + + def _toggle_cover_browser(self, show=None): + if show is None: + show = not self.is_cover_browser_visible + self.do_toggle_cover_browser(show) + self.view_status_changed('cover_browser', show) + + def external_cover_flow_finished(self, *args): + self.view_status_changed('cover_browser', False) + + def _toggle_tag_browser(self, show=None): + self.horizontal_splitter.toggle_side_index() + + def _toggle_book_info(self, show=None): + self.vertical_splitter.toggle_side_index() + + def jobs(self): + src = unicode(self.jobs_button.jobs.text()) + return int(re.search(r'\d+', src).group()) + + def job_added(self, nnum): + jobs = self.jobs_button.jobs + src = unicode(jobs.text()) + num = self.jobs() + text = src.replace(str(num), str(nnum)) + jobs.setText(text) + self.jobs_button.start() + + def job_done(self, nnum): + jobs = self.jobs_button.jobs + src = unicode(jobs.text()) + num = self.jobs() + text = src.replace(str(num), str(nnum)) + jobs.setText(text) + if nnum == 0: + self.no_more_jobs() + + def no_more_jobs(self): + if self.jobs_button.is_running: + self.jobs_button.stop() + QCoreApplication.instance().alert(self, 5000) + + diff --git a/src/calibre/gui2/status.py b/src/calibre/gui2/status.py index a66b903a5e..f7bafacf8b 100644 --- a/src/calibre/gui2/status.py +++ b/src/calibre/gui2/status.py @@ -1,15 +1,14 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' -import os, re, collections +import os, collections from PyQt4.QtGui import QStatusBar, QLabel, QWidget, QHBoxLayout, QPixmap, \ - QVBoxLayout, QSizePolicy, QToolButton, QIcon, QScrollArea, QFrame -from PyQt4.QtCore import Qt, QSize, SIGNAL, QCoreApplication, pyqtSignal + QSizePolicy, QScrollArea +from PyQt4.QtCore import Qt, QSize, pyqtSignal from calibre import fit_image, preferred_encoding, isosx from calibre.gui2 import config from calibre.gui2.widgets import IMAGE_EXTENSIONS -from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.notify import get_notifier from calibre.ebooks import BOOK_EXTENSIONS from calibre.library.comments import comments_to_html @@ -17,6 +16,7 @@ from calibre.library.comments import comments_to_html class BookInfoDisplay(QWidget): DROPABBLE_EXTENSIONS = IMAGE_EXTENSIONS+BOOK_EXTENSIONS + files_dropped = pyqtSignal(object, object) @classmethod def paths_from_event(cls, event): @@ -40,8 +40,7 @@ class BookInfoDisplay(QWidget): def dropEvent(self, event): paths = self.paths_from_event(event) event.setDropAction(Qt.CopyAction) - self.emit(SIGNAL('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event, - paths) + self.files_dropped.emit(event, paths) def dragMoveEvent(self, event): event.acceptProposedAction() @@ -87,6 +86,9 @@ class BookInfoDisplay(QWidget): class BookDataDisplay(QLabel): + + mr = pyqtSignal(int) + def __init__(self): QLabel.__init__(self) self.setText('') @@ -94,7 +96,7 @@ class BookInfoDisplay(QWidget): self.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) def mouseReleaseEvent(self, ev): - self.emit(SIGNAL('mr(int)'), 1) + self.mr.emit(1) WEIGHTS = collections.defaultdict(lambda : 100) WEIGHTS[_('Path')] = 0 @@ -103,6 +105,8 @@ class BookInfoDisplay(QWidget): WEIGHTS[_('Series')] = 2 WEIGHTS[_('Tags')] = 3 + show_book_info = pyqtSignal() + def __init__(self, clear_message): QWidget.__init__(self) self.setCursor(Qt.PointingHandCursor) @@ -113,14 +117,14 @@ class BookInfoDisplay(QWidget): self.cover_display = BookInfoDisplay.BookCoverDisplay() self._layout.addWidget(self.cover_display) self.book_data = BookInfoDisplay.BookDataDisplay() - self.connect(self.book_data, SIGNAL('mr(int)'), self.mouseReleaseEvent) + self.book_data.mr.connect(self.mouseReleaseEvent) self._layout.addWidget(self.book_data) self.data = {} self.setVisible(False) self._layout.setAlignment(self.cover_display, Qt.AlignTop|Qt.AlignLeft) def mouseReleaseEvent(self, ev): - self.emit(SIGNAL('show_book_info()')) + self.show_book_info.emit() def show_data(self, data): if data.has_key('cover'): @@ -128,7 +132,7 @@ class BookInfoDisplay(QWidget): else: self.cover_display.setPixmap(self.cover_display.default_pixmap) - rows = u'' + rows, comments = [], '' self.book_data.setText('') self.data = data.copy() keys = data.keys() @@ -142,97 +146,43 @@ class BookInfoDisplay(QWidget): if isinstance(txt, str): txt = txt.decode(preferred_encoding, 'replace') if key == _('Comments'): - txt = comments_to_html(txt) - rows += u'%s:%s'%(key, txt) - self.book_data.setText(u''+rows+u'
') + comments = comments_to_html(txt) + else: + rows.append((key, txt)) + rows = '\n'.join([u'%s:%s'%(k,t) for + k, t in rows]) + if comments: + comments = 'Comments:'+comments + left_pane = u'%s
'%rows + right_pane = u'
%s
'%comments + self.book_data.setText(u'
%s%s
' + % (left_pane, right_pane)) self.clear_message() self.book_data.updateGeometry() self.updateGeometry() self.setVisible(True) -class MovieButton(QFrame): - - def __init__(self, jobs_dialog): - QFrame.__init__(self) - self.setLayout(QVBoxLayout()) - self.pi = ProgressIndicator(self) - self.layout().addWidget(self.pi) - self.jobs = QLabel(''+_('Jobs:')+' 0') - self.jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom) - self.layout().addWidget(self.jobs) - self.layout().setAlignment(self.jobs, Qt.AlignHCenter) - self.jobs.setMargin(0) - self.layout().setMargin(0) - self.jobs.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) - self.jobs_dialog = jobs_dialog - self.setCursor(Qt.PointingHandCursor) - self.setToolTip(_('Click to see list of active jobs.')) - self.jobs_dialog.jobs_view.restore_column_widths() - - def mouseReleaseEvent(self, event): - if self.jobs_dialog.isVisible(): - self.jobs_dialog.jobs_view.write_settings() - self.jobs_dialog.hide() - else: - self.jobs_dialog.jobs_view.read_settings() - self.jobs_dialog.show() - self.jobs_dialog.jobs_view.restore_column_widths() - - @property - def is_running(self): - return self.pi.isAnimated() - - def start(self): - self.pi.startAnimation() - - def stop(self): - self.pi.stopAnimation() - - -class CoverFlowButton(QToolButton): - - def __init__(self, parent=None): - QToolButton.__init__(self, parent) - self.setIconSize(QSize(80, 80)) - self.setIcon(QIcon(I('cover_flow.svg'))) - self.setCheckable(True) - self.setChecked(False) - self.setAutoRaise(True) - self.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding)) - self.connect(self, SIGNAL('toggled(bool)'), self.adjust_tooltip) - self.adjust_tooltip(False) - self.setCursor(Qt.PointingHandCursor) - - def adjust_tooltip(self, on): - tt = _('Click to turn off Cover Browsing') if on else _('Click to browse books by their covers') - self.setToolTip(tt) - - def disable(self, reason): - self.setDisabled(True) - self.setToolTip(_('

Browsing books by their covers is disabled.
Import of pictureflow module failed:
')+reason) - class StatusBar(QStatusBar): resized = pyqtSignal(object) + files_dropped = pyqtSignal(object, object) + show_book_info = pyqtSignal() - def initialize(self, jobs_dialog, systray=None): + def initialize(self, systray=None): self.systray = systray self.notifier = get_notifier(systray) - self.movie_button = MovieButton(jobs_dialog) - self.cover_flow_button = CoverFlowButton() - self.addPermanentWidget(self.cover_flow_button) - self.addPermanentWidget(self.movie_button) self.book_info = BookInfoDisplay(self.clearMessage) self.book_info.setAcceptDrops(True) self.scroll_area = QScrollArea() self.scroll_area.setWidget(self.book_info) self.scroll_area.setWidgetResizable(True) - self.connect(self.book_info, SIGNAL('show_book_info()'), self.show_book_info) - self.connect(self.book_info, - SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'), - self.files_dropped, Qt.QueuedConnection) + self.book_info.show_book_info.connect(self.show_book_info.emit, + type=Qt.QueuedConnection) + self.book_info.files_dropped.connect(self.files_dropped.emit, + type=Qt.QueuedConnection) self.addWidget(self.scroll_area, 100) self.setMinimumHeight(120) self.resized.connect(self.book_info.cover_display.relayout) @@ -241,10 +191,6 @@ class StatusBar(QStatusBar): def resizeEvent(self, ev): self.resized.emit(self.size()) - def files_dropped(self, event, paths): - self.emit(SIGNAL('files_dropped(PyQt_PyObject, PyQt_PyObject)'), event, - paths) - def reset_info(self): self.book_info.show_data({}) @@ -259,33 +205,4 @@ class StatusBar(QStatusBar): self.notifier(msg) return ret - def jobs(self): - src = unicode(self.movie_button.jobs.text()) - return int(re.search(r'\d+', src).group()) - - def show_book_info(self): - self.emit(SIGNAL('show_book_info()')) - - def job_added(self, nnum): - jobs = self.movie_button.jobs - src = unicode(jobs.text()) - num = self.jobs() - text = src.replace(str(num), str(nnum)) - jobs.setText(text) - self.movie_button.start() - - def job_done(self, nnum): - jobs = self.movie_button.jobs - src = unicode(jobs.text()) - num = self.jobs() - text = src.replace(str(num), str(nnum)) - jobs.setText(text) - if nnum == 0: - self.no_more_jobs() - - def no_more_jobs(self): - if self.movie_button.is_running: - self.movie_button.stop() - QCoreApplication.instance().alert(self, 5000) - diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 441ed18a9b..b35270f963 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -262,16 +262,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): SIGNAL('update_found(PyQt_PyObject)'), self.update_found) self.update_checker.start(2000) ####################### Status Bar ##################### - self.status_bar.initialize(self.jobs_dialog, self.system_tray_icon) - #self.setStatusBar(self.status_bar) - QObject.connect(self.job_manager, SIGNAL('job_added(int)'), - self.status_bar.job_added, Qt.QueuedConnection) - QObject.connect(self.job_manager, SIGNAL('job_done(int)'), - self.status_bar.job_done, Qt.QueuedConnection) - QObject.connect(self.status_bar, SIGNAL('show_book_info()'), - self.show_book_info) - QObject.connect(self.status_bar, SIGNAL('files_dropped(PyQt_PyObject,PyQt_PyObject)'), - self.files_dropped_on_book) + self.status_bar.initialize(self.system_tray_icon) + self.status_bar.show_book_info.connect(self.show_book_info) + self.status_bar.files_dropped.connect(self.files_dropped_on_book) + ####################### Setup Toolbar ##################### md = QMenu() md.addAction(_('Edit metadata individually')) @@ -459,6 +453,10 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): QObject.connect(self.advanced_search_button, SIGNAL('clicked(bool)'), self.do_advanced_search) + for ch in self.tool_bar.children(): + if isinstance(ch, QToolButton): + ch.setCursor(Qt.PointingHandCursor) + ####################### Library view ######################## similar_menu = QMenu(_('Similar books...')) similar_menu.addAction(self.action_books_by_same_author) @@ -554,12 +552,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.cover_cache = CoverCache(self.library_path) self.cover_cache.start() self.library_view.model().cover_cache = self.cover_cache - self.tags_view.setVisible(False) - self.tag_match.setVisible(False) - self.popularity.setVisible(False) - self.restriction_label.setVisible(False) - self.edit_categories.setVisible(False) - self.search_restriction.setVisible(False) self.connect(self.edit_categories, SIGNAL('clicked()'), self.do_edit_categories) self.tags_view.set_database(db, self.tag_match, self.popularity, self.search_restriction) self.connect(self.tags_view, @@ -626,22 +618,28 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if not config['separate_cover_flow']: self.library.layout().addWidget(self.cover_flow) self.cover_flow.currentChanged.connect(self.sync_listview_to_cf) - self.connect(self.status_bar.cover_flow_button, - SIGNAL('toggled(bool)'), self.toggle_cover_flow) - self.connect(self.cover_flow, SIGNAL('stop()'), - self.status_bar.cover_flow_button.toggle) self.library_view.selectionModel().currentRowChanged.connect( self.sync_cf_to_listview) self.db_images = DatabaseImages(self.library_view.model()) self.cover_flow.setImages(self.db_images) - else: - self.status_bar.cover_flow_button.disable(pictureflowerror) self._calculated_available_height = min(max_available_height()-15, self.height()) self.resize(self.width(), self._calculated_available_height) self.search.setMaximumWidth(self.width()-150) + ####################### Side Bar ############################### + + self.sidebar.initialize(self.jobs_dialog, self.cover_flow, + self.toggle_cover_flow, pictureflowerror, + self.vertical_splitter, self.horizontal_splitter) + QObject.connect(self.job_manager, SIGNAL('job_added(int)'), + self.sidebar.job_added, Qt.QueuedConnection) + QObject.connect(self.job_manager, SIGNAL('job_done(int)'), + self.sidebar.job_done, Qt.QueuedConnection) + + + if config['autolaunch_server']: from calibre.library.server import start_threaded_server from calibre.library import server_config @@ -668,19 +666,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.location_view.setCurrentIndex(self.location_view.model().index(0)) - if self.cover_flow is not None and dynamic.get('cover_flow_visible', False): - self.status_bar.cover_flow_button.toggle() - - tb_state = dynamic.get('tag_browser_state', None) - if tb_state is not None: - self.horizontal_splitter.restoreState(tb_state) - self.toggle_tags_view(True) - - bi_state = dynamic.get('book_info_state', None) - if bi_state is not None: - self.vertical_splitter.restoreState(bi_state) - self.horizontal_splitter.initialize() - self.vertical_splitter.initialize() self._add_filesystem_book = Dispatcher(self.__add_filesystem_book) v = self.library_view @@ -782,11 +767,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): if search: self.search.set_search_string(join.join(search)) - - - def uncheck_cover_button(self, *args): - self.status_bar.cover_flow_button.setChecked(False) - def toggle_cover_flow(self, show): if config['separate_cover_flow']: if show: @@ -802,8 +782,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.cover_flow.setFocus(Qt.OtherFocusReason) self.library_view.scrollTo(self.library_view.currentIndex()) d.show() - self.connect(d, SIGNAL('finished(int)'), - self.uncheck_cover_button) + d.finished.connect(self.sidebar.external_cover_flow_finished) self.cf_dialog = d self.cover_flow_sync_timer.start(500) else: @@ -825,8 +804,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.library_view.currentIndex()) self.cover_flow.setVisible(True) self.cover_flow.setFocus(Qt.OtherFocusReason) - #self.status_bar.book_info.book_data.setMaximumHeight(100) - #self.status_bar.setMaximumHeight(120) self.library_view.scrollTo(self.library_view.currentIndex()) self.cover_flow_sync_timer.start(500) else: @@ -837,26 +814,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): sm = self.library_view.selectionModel() sm.select(idx, sm.ClearAndSelect|sm.Rows) self.library_view.setCurrentIndex(idx) - #self.status_bar.book_info.book_data.setMaximumHeight(1000) - #self.resize(self.width(), self._calculated_available_height) - #self.setMaximumHeight(available_height()) - def toggle_tags_view(self, show): - if show: - self.tags_view.setVisible(True) - self.tag_match.setVisible(True) - self.popularity.setVisible(True) - self.restriction_label.setVisible(True) - self.edit_categories.setVisible(True) - self.search_restriction.setVisible(True) - self.tags_view.setFocus(Qt.OtherFocusReason) - else: - self.tags_view.setVisible(False) - self.tag_match.setVisible(False) - self.popularity.setVisible(False) - self.restriction_label.setVisible(False) - self.edit_categories.setVisible(False) - self.search_restriction.setVisible(False) + ''' Handling of the count of books in a restricted view requires that @@ -2330,6 +2289,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): view.resizeColumnsToContents() view.resize_on_select = False self.status_bar.reset_info() + self.sidebar.location_changed(location) if location == 'library': self.action_edit.setEnabled(True) self.action_merge.setEnabled(True) @@ -2337,7 +2297,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.view_menu.actions()[1].setEnabled(True) self.action_open_containing_folder.setEnabled(True) self.action_sync.setEnabled(True) - self.status_bar.cover_flow_button.setEnabled(True) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(True) else: @@ -2347,7 +2306,6 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): self.view_menu.actions()[1].setEnabled(False) self.action_open_containing_folder.setEnabled(False) self.action_sync.setEnabled(False) - self.status_bar.cover_flow_button.setEnabled(False) for action in list(self.delete_menu.actions())[1:]: action.setEnabled(False) @@ -2463,11 +2421,7 @@ class Main(MainWindow, Ui_MainWindow, DeviceGUI): def write_settings(self): config.set('main_window_geometry', self.saveGeometry()) dynamic.set('sort_history', self.library_view.model().sort_history) - dynamic.set('cover_flow_visible', self.cover_flow.isVisible()) - dynamic.set('tag_browser_state', - str(self.horizontal_splitter.saveState())) - dynamic.set('book_info_state', - str(self.vertical_splitter.saveState())) + self.sidebar.save_state() self.library_view.write_settings() if self.device_connected: self.save_device_view_settings() diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index e39b06ea54..db5f222408 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -982,6 +982,12 @@ class SplitterHandle(QSplitterHandle): class Splitter(QSplitter): + state_changed = pyqtSignal(object) + + def __init__(self, *args): + QSplitter.__init__(self, *args) + self.splitterMoved.connect(self.splitter_moved, type=Qt.QueuedConnection) + def createHandle(self): return SplitterHandle(self.orientation(), self) @@ -990,6 +996,22 @@ class Splitter(QSplitter): h = self.handle(i) if h is not None: h.splitter_moved() + self.state_changed.emit(not self.is_side_index_hidden) + + def splitter_moved(self, *args): + self.state_changed.emit(not self.is_side_index_hidden) + + @property + def side_index(self): + return 0 if self.orientation() == Qt.Horizontal else 1 + + @property + def is_side_index_hidden(self): + sizes = list(self.sizes()) + return sizes[self.side_index] == 0 + + def toggle_side_index(self): + self.double_clicked(None) def double_clicked(self, handle): sizes = list(self.sizes()) @@ -997,8 +1019,7 @@ class Splitter(QSplitter): idx = sizes.index(0) sizes[idx] = 80 else: - idx = 0 if self.orientation() == Qt.Horizontal else 1 - sizes[idx] = 0 + sizes[self.side_index] = 0 self.setSizes(sizes) self.initialize()