calibre now has two layout modes - wide and narrow, chooseable from Preferences->Interface. The default is wide, the old layout is narrow. Fixes #5756 (Some small layout changes)

This commit is contained in:
Kovid Goyal 2010-06-14 01:06:53 -06:00
parent 5303ee2fc7
commit 65e12335b8
10 changed files with 2979 additions and 197 deletions

View File

@ -100,7 +100,7 @@ def _config():
c.add_opt('tag_browser_hidden_categories', default=set(),
help=_('tag browser categories not to display'))
c.add_opt('gui_layout', choices=['wide', 'narrow'],
help=_('The layout of the user interface'), default='narrow')
help=_('The layout of the user interface'), default='wide')
return ConfigProxy(c)
config = _config()

View File

@ -8,16 +8,18 @@ __docformat__ = 'restructuredtext en'
import os, collections
from PyQt4.Qt import QLabel, QPixmap, QSize, QWidget, Qt, pyqtSignal, \
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve
QVBoxLayout, QScrollArea, QPropertyAnimation, QEasingCurve, \
QSizePolicy, QPainter, QRect, pyqtProperty
from calibre import fit_image, prepare_string_for_xml
from calibre.gui2.widgets import IMAGE_EXTENSIONS
from calibre.ebooks import BOOK_EXTENSIONS
from calibre.constants import preferred_encoding
from calibre.library.comments import comments_to_html
# render_rows(data) {{{
WEIGHTS = collections.defaultdict(lambda : 100)
WEIGHTS[_('Path')] = 0
WEIGHTS[_('Path')] = 5
WEIGHTS[_('Formats')] = 1
WEIGHTS[_('Collections')] = 2
WEIGHTS[_('Series')] = 3
@ -29,7 +31,7 @@ def render_rows(data):
rows = []
for key in keys:
txt = data[key]
if key in ('id', _('Comments')) or not txt or not txt.strip() or \
if key in ('id', _('Comments')) or not hasattr(txt, 'strip') or not txt.strip() or \
txt == 'None':
continue
if isinstance(key, str):
@ -41,7 +43,8 @@ def render_rows(data):
if 'id' in data:
if key == _('Path'):
txt = '...'+os.sep+os.sep.join(txt.split(os.sep)[-2:])
txt = u'<a href="path:%s">%s</a>'%(data['id'], txt)
txt = u'<a href="path:%s">%s</a>'%(data['id'],
_('Click to open'))
if key == _('Formats') and txt and txt != _('None'):
fmts = [x.strip() for x in txt.split(',')]
fmts = [u'<a href="format:%s:%s">%s</a>' % (data['id'], x, x) for x
@ -52,53 +55,91 @@ def render_rows(data):
# }}}
class CoverView(QLabel): # {{{
class CoverView(QWidget): # {{{
def __init__(self, parent=None):
QLabel.__init__(self, parent)
self.animation = QPropertyAnimation(self, 'size', self)
QWidget.__init__(self, parent)
self.setMaximumSize(QSize(120, 120))
self.setMinimumSize(QSize(120, 1))
self._current_pixmap_size = self.maximumSize()
self.animation = QPropertyAnimation(self, 'current_pixmap_size', self)
self.animation.setEasingCurve(QEasingCurve(QEasingCurve.OutExpo))
self.animation.setDuration(1000)
self.animation.setStartValue(QSize(0, 0))
self.animation.valueChanged.connect(self.value_changed)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
self.default_pixmap = QPixmap(I('book.svg'))
self.max_width, self.max_height = 120, 120
self.setScaledContents(True)
self.setPixmap(self.default_pixmap)
self.pixmap = self.default_pixmap
self.pwidth = self.pheight = None
self.data = {}
def do_layout(self):
pixmap = self.pixmap()
pwidth, pheight = pixmap.width(), pixmap.height()
width, height = fit_image(pwidth, pheight,
self.max_width, self.max_height)[1:]
self.setMaximumWidth(width)
try:
aspect_ratio = pwidth/float(pheight)
except ZeroDivisionError:
aspect_ratio = 1
mh = min(self.max_height, int(width/aspect_ratio))
self.setMaximumHeight(mh)
self.animation.setEndValue(self.maximumSize())
self.do_layout()
def setPixmap(self, pixmap):
QLabel.setPixmap(self, pixmap)
self.do_layout()
self.animation.start()
def value_changed(self, val):
self.update()
def setCurrentPixmapSize(self, val):
self._current_pixmap_size = val
def sizeHint(self):
return QSize(self.maximumWidth(), self.maximumHeight())
def do_layout(self):
pixmap = self.pixmap
pwidth, pheight = pixmap.width(), pixmap.height()
self.pwidth, self.pheight = fit_image(pwidth, pheight,
self.maximumWidth(), self.maximumHeight())[1:]
self.current_pixmap_size = QSize(self.pwidth, self.pheight)
self.animation.setEndValue(self.current_pixmap_size)
def relayout(self, parent_size):
self.max_height = int(parent_size.height()/3.)
self.max_width = parent_size.width()
self.do_layout()
def relayout(self, parent_size):
self.setMaximumSize(parent_size.width(),
int(parent_size.height()/3.)+1)
self.resize(self.maximumSize())
self.animation.stop()
self.do_layout()
def sizeHint(self):
return self.maximumSize()
def show_data(self, data):
self.animation.stop()
if data.get('id', None) == self.data.get('id', None):
return
self.data = {'id':data.get('id', None)}
if data.has_key('cover'):
self.pixmap = QPixmap.fromImage(data.pop('cover'))
if self.pixmap.isNull():
self.pixmap = self.default_pixmap
else:
self.pixmap = self.default_pixmap
self.do_layout()
self.update()
self.animation.start()
def paintEvent(self, event):
canvas_size = self.rect()
width = self.current_pixmap_size.width()
extrax = canvas_size.width() - width
if extrax < 0: extrax = 0
x = int(extrax/2.)
height = self.current_pixmap_size.height()
extray = canvas_size.height() - height
if extray < 0: extray = 0
y = int(extray/2.)
target = QRect(x, y, width, height)
p = QPainter(self)
p.setRenderHints(QPainter.Antialiasing | QPainter.SmoothPixmapTransform)
p.drawPixmap(target, self.pixmap.scaled(target.size(),
Qt.KeepAspectRatio, Qt.SmoothTransformation))
p.end()
current_pixmap_size = pyqtProperty('QSize',
fget=lambda self: self._current_pixmap_size,
fset=setCurrentPixmapSize
)
def show_data(self, data):
if data.has_key('cover'):
self.setPixmap(QPixmap.fromImage(data.pop('cover')))
else:
self.setPixmap(self.default_pixmap)
# }}}
@ -108,10 +149,12 @@ class Label(QLabel):
link_clicked = pyqtSignal(object)
def __init__(self):
QLabel.__init__(self)
self.setText('')
self.setWordWrap(True)
self.linkActivated.connect(self.link_activated)
self._link_clicked = False
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
def link_activated(self, link):
self._link_clicked = True
@ -133,13 +176,17 @@ class BookInfo(QScrollArea):
self.setWidget(self.label)
self.link_clicked = self.label.link_clicked
self.mr = self.label.mr
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def show_data(self, data):
self.label.setText('')
self.data = data.copy()
rows = render_rows(self.data)
rows = render_rows(data)
rows = u'\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') in data and data[_('Comments')]:
comments = comments_to_html(data[_('Comments')])
rows += u'<tr><td colspan="2">%s</td></tr>'%comments
self.label.setText(u'<table>%s</table>'%rows)
class BookDetails(QWidget):
@ -188,13 +235,14 @@ class BookDetails(QWidget):
self.setLayout(self._layout)
self.cover_view = CoverView(self)
self.cover_view.relayout()
self.cover_view.relayout(self.size())
self.resized.connect(self.cover_view.relayout, type=Qt.QueuedConnection)
self._layout.addWidget(self.cover_view)
self._layout.addWidget(self.cover_view, alignment=Qt.AlignHCenter)
self.book_info = BookInfo(self)
self._layout.addWidget(self.book_info)
self.book_info.link_clicked.connect(self._link_clicked)
self.book_info.mr.connect(self.mouseReleaseEvent)
self.setMinimumSize(QSize(190, 200))
def _link_clicked(self, link):
typ, _, val = link.partition(':')
@ -219,3 +267,4 @@ class BookDetails(QWidget):
self.show_data({})

View File

@ -483,6 +483,14 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.port.editingFinished.connect(self.check_port_value)
self.show_splash_screen.setChecked(gprefs.get('show_splash_screen',
True))
li = None
for i, z in enumerate([('wide', _('Wide')),
('narrow', _('Narrow'))]):
x, y = z
self.opt_gui_layout.addItem(y, QVariant(x))
if x == config['gui_layout']:
li = i
self.opt_gui_layout.setCurrentIndex(li)
def check_port_value(self, *args):
port = self.port.value()
@ -863,6 +871,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
if self.viewer.item(i).checkState() == Qt.Checked:
fmts.append(str(self.viewer.item(i).text()))
config['internally_viewed_formats'] = fmts
val = self.opt_gui_layout.itemData(self.opt_gui_layout.currentIndex()).toString()
config['gui_layout'] = unicode(val)
if not path or not os.path.exists(path) or not os.path.isdir(path):
d = error_dialog(self, _('Invalid database location'),

View File

@ -89,8 +89,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>608</width>
<height>683</height>
<width>604</width>
<height>679</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_7">
@ -332,7 +332,7 @@
</widget>
<widget class="QWidget" name="page">
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="roman_numerals">
<property name="text">
<string>Use &amp;Roman numerals for series number</string>
@ -342,35 +342,35 @@
</property>
</widget>
</item>
<item row="1" column="0">
<item row="2" column="0">
<widget class="QCheckBox" name="systray_icon">
<property name="text">
<string>Enable system &amp;tray icon (needs restart)</string>
</property>
</widget>
</item>
<item row="1" column="1">
<item row="2" column="1">
<widget class="QCheckBox" name="systray_notifications">
<property name="text">
<string>Show &amp;notifications in system tray</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="show_splash_screen">
<property name="text">
<string>Show &amp;splash screen at startup</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<item row="4" column="0" colspan="2">
<widget class="QCheckBox" name="separate_cover_flow">
<property name="text">
<string>Show cover &amp;browser in a separate window (needs restart)</string>
</property>
</widget>
</item>
<item row="4" column="0">
<item row="5" column="0">
<widget class="QCheckBox" name="search_as_you_type">
<property name="text">
<string>Search as you type</string>
@ -380,21 +380,21 @@
</property>
</widget>
</item>
<item row="5" column="0" colspan="2">
<item row="6" column="0" colspan="2">
<widget class="QCheckBox" name="sync_news">
<property name="text">
<string>Automatically send downloaded &amp;news to ebook reader</string>
</property>
</widget>
</item>
<item row="6" column="0" colspan="2">
<item row="7" column="0" colspan="2">
<widget class="QCheckBox" name="delete_news">
<property name="text">
<string>&amp;Delete news from library when it is automatically sent to reader</string>
</property>
</widget>
</item>
<item row="7" column="0" colspan="2">
<item row="8" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_6">
@ -411,7 +411,7 @@
</item>
</layout>
</item>
<item row="8" column="0" colspan="2">
<item row="9" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Toolbar</string>
@ -459,7 +459,7 @@
</layout>
</widget>
</item>
<item row="9" column="0" colspan="2">
<item row="10" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QGroupBox" name="groupBox">
@ -625,6 +625,26 @@
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_17">
<property name="text">
<string>User Interface &amp;layout (needs restart):</string>
</property>
<property name="buddy">
<cstring>opt_gui_layout</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="opt_gui_layout">
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_6">

View File

@ -17,6 +17,8 @@ from calibre.gui2 import config, is_widescreen
from calibre.gui2.library.views import BooksView, DeviceBooksView
from calibre.gui2.widgets import Splitter
from calibre.gui2.tag_view import TagBrowserWidget
from calibre.gui2.status import StatusBar, HStatusBar
from calibre.gui2.book_details import BookDetails
_keep_refs = []
@ -290,9 +292,9 @@ class LibraryViewMixin(object): # {{{
class LibraryWidget(Splitter): # {{{
def __init__(self, parent):
orientation = Qt.Vertical if config['gui_layout'] == 'narrow' and \
not is_widescreen() else Qt.Horizontal
#orientation = Qt.Vertical
orientation = Qt.Vertical
if config['gui_layout'] == 'narrow':
orientation = Qt.Horizontal if is_widescreen() else Qt.Vertical
idx = 0 if orientation == Qt.Vertical else 1
size = 300 if orientation == Qt.Vertical else 550
Splitter.__init__(self, 'cover_browser_splitter', _('Cover Browser'),
@ -360,7 +362,6 @@ class LayoutMixin(object): # {{{
self.setWindowTitle(__appname__)
if config['gui_layout'] == 'narrow':
from calibre.gui2.status import StatusBar
self.status_bar = self.book_details = StatusBar(self)
self.stack = Stack(self)
self.bd_splitter = Splitter('book_details_splitter',
@ -375,8 +376,28 @@ class LayoutMixin(object): # {{{
l.addWidget(self.sidebar)
self.bd_splitter.addWidget(self._layout_mem[0])
self.bd_splitter.addWidget(self.status_bar)
self.bd_splitter.setCollapsible((self.bd_splitter.side_index+1)%2, False)
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
self.centralwidget.layout().addWidget(self.bd_splitter)
else:
self.status_bar = HStatusBar(self)
self.setStatusBar(self.status_bar)
self.bd_splitter = Splitter('book_details_splitter',
_('Book Details'), I('book.svg'), initial_side_size=200,
orientation=Qt.Horizontal, parent=self, side_index=1)
self.stack = Stack(self)
self.bd_splitter.addWidget(self.stack)
self.book_details = BookDetails(self)
self.bd_splitter.addWidget(self.book_details)
self.bd_splitter.setCollapsible(self.bd_splitter.other_index, False)
self.bd_splitter.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
QSizePolicy.Expanding))
self.centralwidget.layout().addWidget(self.bd_splitter)
for x in ('cb', 'tb', 'bd'):
button = getattr(self, x+'_splitter').button
button.setIconSize(QSize(22, 22))
self.status_bar.addPermanentWidget(button)
self.status_bar.addPermanentWidget(self.jobs_button)
def finalize_layout(self):
m = self.library_view.model()

View File

@ -274,11 +274,15 @@ class JobsButton(QFrame):
def __init__(self, horizontal=False, size=48, parent=None):
QFrame.__init__(self, parent)
if horizontal:
size = 24
self.pi = ProgressIndicator(self, size)
self._jobs = QLabel('<b>'+_('Jobs:')+' 0')
self._jobs.mouseReleaseEvent = self.mouseReleaseEvent
if horizontal:
self.setLayout(QHBoxLayout())
self.layout().setDirection(self.layout().RightToLeft)
else:
self.setLayout(QVBoxLayout())
self._jobs.setAlignment(Qt.AlignHCenter|Qt.AlignBottom)

View File

@ -554,7 +554,7 @@ void PictureFlowPrivate::resize(int w, int h)
if (h < 10) h = 10;
slideHeight = int(float(h)/REFLECTION_FACTOR);
slideWidth = int(float(slideHeight) * 2/3.);
fontSize = MAX(int(h/20.), 12);
fontSize = MAX(int(h/15.), 12);
recalc(w, h);
resetSlides();
triggerRender();

View File

@ -152,7 +152,7 @@ class BookInfoDisplay(QWidget):
k, t in rows])
if _('Comments') in self.data:
comments = comments_to_html(self.data[_('Comments')])
comments = '<b>Comments:</b>'+comments
comments = ('<b>%s:</b>'%_('Comments'))+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" '
@ -197,6 +197,9 @@ class BookDetailsInterface(object):
def show_data(self, data):
raise NotImplementedError()
class HStatusBar(QStatusBar, StatusBarInterface):
pass
class StatusBar(QStatusBar, StatusBarInterface, BookDetailsInterface):
files_dropped = pyqtSignal(object, object)

View File

@ -126,7 +126,8 @@ class Main(MainWindow, Ui_MainWindow, DeviceMixin, ToolbarMixin, # {{{
# Jobs Button {{{
self.job_manager = JobManager()
self.jobs_dialog = JobsDialog(self, self.job_manager)
self.jobs_button = JobsButton()
self.jobs_button = JobsButton(horizontal=config['gui_layout'] !=
'narrow')
self.jobs_button.initialize(self.jobs_dialog, self.job_manager)
# }}}

File diff suppressed because it is too large Load Diff