Add a sidebar to the main GUI to control the optional views (tag browser, book info and cover browser)

This commit is contained in:
Kovid Goyal 2010-05-03 20:00:39 -06:00
parent 8f93da127f
commit 1718859647
5 changed files with 589 additions and 442 deletions

View File

@ -150,7 +150,7 @@
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<layout class="QHBoxLayout" name="hl234">
<property name="spacing">
<number>6</number>
</property>
@ -288,271 +288,285 @@
</layout>
</item>
<item>
<widget class="Splitter" name="vertical_splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QStackedWidget" name="stack">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="library">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="Splitter" name="horizontal_splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="TagsView" name="tags_view">
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="popularity">
<property name="text">
<string>Sort by &amp;popularity</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="Splitter" name="vertical_splitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QStackedWidget" name="stack">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>100</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="library">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="Splitter" name="horizontal_splitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QWidget" name="layoutWidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QComboBox" name="tag_match">
<property name="currentIndex">
<number>0</number>
<widget class="TagsView" name="tags_view">
<property name="tabKeyNavigation">
<bool>true</bool>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="animated">
<bool>true</bool>
</property>
<property name="headerHidden">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="popularity">
<property name="text">
<string>Sort by &amp;popularity</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<property name="text">
<string>Match any</string>
</property>
<widget class="QComboBox" name="tag_match">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<property name="text">
<string>Match any</string>
</property>
</item>
<item>
<property name="text">
<string>Match all</string>
</property>
</item>
</widget>
</item>
<item>
<property name="text">
<string>Match all</string>
</property>
<widget class="QPushButton" name="edit_categories">
<property name="toolTip">
<string>Create, edit, and delete user categories</string>
</property>
<property name="text">
<string>Manage &amp;user categories</string>
</property>
</widget>
</item>
</widget>
</layout>
</item>
<item>
<widget class="QPushButton" name="edit_categories">
<property name="toolTip">
<string>Create, edit, and delete user categories</string>
</property>
<property name="text">
<string>Manage &amp;user categories</string>
</property>
</widget>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="10,50">
<item>
<widget class="QLabel" name="restriction_label">
<property name="text">
<string>&amp;Restrict to:</string>
</property>
<property name="buddy">
<cstring>search_restriction</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="search_restriction">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>50</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Books display will be restricted to those matching the selected saved search</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="10,50">
<item>
<widget class="QLabel" name="restriction_label">
<property name="text">
<string>&amp;Restrict to:</string>
</property>
<property name="buddy">
<cstring>search_restriction</cstring>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="search_restriction">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>50</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Books display will be restricted to those matching the selected saved search</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="BooksView" name="library_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="BooksView" name="library_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="main_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="memory_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="card_a_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="card_a_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>10</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="card_b_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="card_b_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>10</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="StatusBar" name="status_bar" native="true"/>
</widget>
<widget class="QWidget" name="main_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="memory_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>100</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="SideBar" name="sidebar" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
<widget class="QWidget" name="card_a_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="card_a_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>10</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="card_b_memory">
<layout class="QGridLayout">
<item row="0" column="0">
<widget class="DeviceBooksView" name="card_b_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>10</horstretch>
<verstretch>10</verstretch>
</sizepolicy>
</property>
<property name="acceptDrops">
<bool>true</bool>
</property>
<property name="dragEnabled">
<bool>true</bool>
</property>
<property name="dragDropOverwriteMode">
<bool>false</bool>
</property>
<property name="dragDropMode">
<enum>QAbstractItemView::DragDrop</enum>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="showGrid">
<bool>false</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="StatusBar" name="status_bar" native="true"/>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -832,6 +846,12 @@
<header>calibre/gui2/widgets.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>SideBar</class>
<extends>QWidget</extends>
<header>calibre/gui2/sidebar.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../resources/images.qrc"/>

235
src/calibre/gui2/sidebar.py Normal file
View File

@ -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 <kovid@kovidgoyal.net>'
__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('<b>'+_('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)

View File

@ -1,15 +1,14 @@
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
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'<tr><td><b>%s:</b></td><td>%s</td></tr>'%(key, txt)
self.book_data.setText(u'<table>'+rows+u'</table>')
comments = comments_to_html(txt)
else:
rows.append((key, txt))
rows = '\n'.join([u'<tr><td valign="top"><b>%s:</b></td><td valign="top">%s</td></tr>'%(k,t) for
k, t in rows])
if comments:
comments = '<b>Comments:</b>'+comments
left_pane = u'<table>%s</table>'%rows
right_pane = u'<div>%s</div>'%comments
self.book_data.setText(u'<table><tr><td valign="top" '
'style="padding-right:2em">%s</td><td valign="top">%s</td></tr></table>'
% (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('<b>'+_('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(_('<p>Browsing books by their covers is disabled.<br>Import of pictureflow module failed:<br>')+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)

View File

@ -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()

View File

@ -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()