Show path information for books in the library and Implement #1013 (Add "Next" and "Previous" buttons in the detailed view)

This commit is contained in:
Kovid Goyal 2008-09-15 14:10:38 -07:00
parent 8c53abe905
commit 5e236b8edb
6 changed files with 142 additions and 54 deletions

View File

@ -5,24 +5,68 @@ __docformat__ = 'restructuredtext en'
''' '''
''' '''
import textwrap import textwrap, os
from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import QCoreApplication, SIGNAL, QModelIndex, QUrl
from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon, QDesktopServices
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
class BookInfo(QDialog, Ui_BookInfo): class BookInfo(QDialog, Ui_BookInfo):
def __init__(self, parent, info): def __init__(self, parent, view, row):
QDialog.__init__(self, parent) QDialog.__init__(self, parent)
Ui_BookInfo.__init__(self) Ui_BookInfo.__init__(self)
self.setupUi(self) self.setupUi(self)
self.setWindowTitle(info[_('Title')])
desktop = QCoreApplication.instance().desktop() desktop = QCoreApplication.instance().desktop()
screen_height = desktop.availableGeometry().height() - 100 screen_height = desktop.availableGeometry().height() - 100
self.resize(self.size().width(), screen_height) self.resize(self.size().width(), screen_height)
self.view = view
self.current_row = None
self.refresh(row)
self.connect(self.view.selectionModel(), SIGNAL('currentChanged(QModelIndex,QModelIndex)'), self.slave)
self.connect(self.next_button, SIGNAL('clicked()'), self.next)
self.connect(self.previous_button, SIGNAL('clicked()'), self.previous)
self.connect(self.text, SIGNAL('linkActivated(QString)'), self.open_book_path)
def slave(self, current, previous):
row = current.row()
self.refresh(row)
def open_book_path(self, path):
if os.sep in unicode(path):
QDesktopServices.openUrl(QUrl('file:'+path))
else:
format = unicode(path)
path = self.view.model().db.format_abspath(self.current_row, format)
if path is not None:
QDesktopServices.openUrl(QUrl('file:'+path))
def next(self):
row = self.view.currentIndex().row()
ni = self.view.model().index(row+1, 0)
if ni.isValid():
self.view.setCurrentIndex(ni)
def previous(self):
row = self.view.currentIndex().row()
ni = self.view.model().index(row-1, 0)
if ni.isValid():
self.view.setCurrentIndex(ni)
def refresh(self, row):
if isinstance(row, QModelIndex):
row = row.row()
if row == self.current_row:
return
self.previous_button.setEnabled(False if row == 0 else True)
self.next_button.setEnabled(False if row == self.view.model().rowCount(QModelIndex())-1 else True)
self.current_row = row
info = self.view.model().get_book_info(row)
self.setWindowTitle(info[_('Title')])
self.title.setText('<b>'+info.pop(_('Title'))) self.title.setText('<b>'+info.pop(_('Title')))
self.comments.setText(info.pop(_('Comments'), '')) self.comments.setText(info.pop(_('Comments'), ''))
@ -37,6 +81,15 @@ class BookInfo(QDialog, Ui_BookInfo):
rows = u'' rows = u''
self.text.setText('') self.text.setText('')
self.data = info self.data = info
if _('Path') in info.keys():
p = info[_('Path')]
info[_('Path')] = '<a href="%s">%s</a>'%(p, p)
if _('Formats') in info.keys():
formats = info[_('Formats')].split(',')
info[_('Formats')] = ''
for f in formats:
f = f.strip()
info[_('Formats')] += '<a href="%s">%s</a>, '%(f,f)
for key in info.keys(): for key in info.keys():
txt = info[key] txt = info[key]
txt = u'<br />\n'.join(textwrap.wrap(txt, 120)) txt = u'<br />\n'.join(textwrap.wrap(txt, 120))

View File

@ -6,14 +6,14 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>917</width> <width>917</width>
<height>780</height> <height>783</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
<string>Dialog</string> <string>Dialog</string>
</property> </property>
<layout class="QVBoxLayout" > <layout class="QGridLayout" name="gridLayout" >
<item> <item row="0" column="0" colspan="2" >
<widget class="QLabel" name="title" > <widget class="QLabel" name="title" >
<property name="text" > <property name="text" >
<string>TextLabel</string> <string>TextLabel</string>
@ -23,20 +23,11 @@
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="1" column="0" >
<widget class="QSplitter" name="splitter" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<widget class="QGraphicsView" name="cover" /> <widget class="QGraphicsView" name="cover" />
<widget class="QWidget" name="" > </item>
<layout class="QVBoxLayout" > <item row="1" column="1" >
<layout class="QVBoxLayout" name="verticalLayout" >
<item> <item>
<widget class="QLabel" name="text" > <widget class="QLabel" name="text" >
<property name="text" > <property name="text" >
@ -62,12 +53,38 @@
</layout> </layout>
</widget> </widget>
</item> </item>
</layout> <item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QPushButton" name="previous_button" >
<property name="text" >
<string>&amp;Previous</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/previous.svg</normaloff>:/images/previous.svg</iconset>
</property>
</widget> </widget>
</item>
<item>
<widget class="QPushButton" name="next_button" >
<property name="text" >
<string>&amp;Next</string>
</property>
<property name="icon" >
<iconset resource="../images.qrc" >
<normaloff>:/images/next.svg</normaloff>:/images/next.svg</iconset>
</property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item>
</layout>
</item>
</layout>
</widget> </widget>
<resources/> <resources>
<include location="../images.qrc" />
</resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -230,6 +230,7 @@ class BooksModel(QAbstractTableModel):
else: else:
formats = _('None') formats = _('None')
data[_('Formats')] = formats data[_('Formats')] = formats
data[_('Path')] = self.db.abspath(idx)
comments = self.db.comments(idx) comments = self.db.comments(idx)
if not comments: if not comments:
comments = _('None') comments = _('None')
@ -265,6 +266,8 @@ class BooksModel(QAbstractTableModel):
return data return data
def get_book_info(self, index): def get_book_info(self, index):
if isinstance(index, int):
index = self.index(index, 0)
data = self.current_changed(index, None, False) data = self.current_changed(index, None, False)
row = index.row() row = index.row()
data[_('Title')] = self.db.title(row) data[_('Title')] = self.db.title(row)

View File

@ -1185,8 +1185,7 @@ in which you want to store your books files. Any existing books will be automati
return return
index = self.library_view.currentIndex() index = self.library_view.currentIndex()
if index.isValid(): if index.isValid():
info = self.library_view.model().get_book_info(index) BookInfo(self, self.library_view, index).show()
BookInfo(self, info).show()
############################################################################ ############################################################################

View File

@ -73,7 +73,7 @@ class BookInfoDisplay(QFrame):
rows = u'' rows = u''
self.book_data.setText('') self.book_data.setText('')
self.data = data self.data = data.copy()
for key in data.keys(): for key in data.keys():
txt = data[key] txt = data[key]
#txt = '<br />\n'.join(textwrap.wrap(txt, 120)) #txt = '<br />\n'.join(textwrap.wrap(txt, 120))

View File

@ -418,10 +418,17 @@ class LibraryDatabase2(LibraryDatabase):
def path(self, index, index_is_id=False): def path(self, index, index_is_id=False):
'Return the relative path to the directory containing this books files as a unicode string.' 'Return the relative path to the directory containing this books files as a unicode string.'
id = index if index_is_id else self.id() id = index if index_is_id else self.id(index)
path = self.conn.execute('SELECT path FROM books WHERE id=?', (id,)).fetchone()[0].replace('/', os.sep) path = self.conn.execute('SELECT path FROM books WHERE id=?', (id,)).fetchone()[0].replace('/', os.sep)
return path return path
def abspath(self, index, index_is_id=False):
'Return the absolute path to the directory containing this books files as a unicode string.'
path = os.path.join(self.library_path, self.path(index, index_is_id=index_is_id))
if not os.path.exists(path):
os.makedirs(path)
return path
def construct_path_name(self, id): def construct_path_name(self, id):
''' '''
@ -550,13 +557,8 @@ class LibraryDatabase2(LibraryDatabase):
return ','.join(ans) return ','.join(ans)
def format(self, index, format, index_is_id=False, as_file=False, mode='r+b'): def format_abspath(self, index, format, index_is_id=False):
''' 'Return absolute path to the ebook file of format `format`'
Return the ebook format as a bytestring or `None` if the format doesn't exist,
or we don't have permission to write to the ebook file.
`as_file`: If True the ebook format is returned as a file object opened in `mode`
'''
id = index if index_is_id else self.id(index) id = index if index_is_id else self.id(index)
path = os.path.join(self.library_path, self.path(id, index_is_id=True)) path = os.path.join(self.library_path, self.path(id, index_is_id=True))
name = self.conn.execute('SELECT name FROM data WHERE book=? AND format=?', (id, format)).fetchone()[0] name = self.conn.execute('SELECT name FROM data WHERE book=? AND format=?', (id, format)).fetchone()[0]
@ -564,6 +566,17 @@ class LibraryDatabase2(LibraryDatabase):
format = ('.' + format.lower()) if format else '' format = ('.' + format.lower()) if format else ''
path = os.path.join(path, name+format) path = os.path.join(path, name+format)
if os.access(path, os.R_OK|os.W_OK): if os.access(path, os.R_OK|os.W_OK):
return path
def format(self, index, format, index_is_id=False, as_file=False, mode='r+b'):
'''
Return the ebook format as a bytestring or `None` if the format doesn't exist,
or we don't have permission to write to the ebook file.
`as_file`: If True the ebook format is returned as a file object opened in `mode`
'''
path = self.format_abspath(index, format, index_is_id=index_is_id)
if path is not None:
f = open(path, mode) f = open(path, mode)
return f if as_file else f.read() return f if as_file else f.read()
self.remove_format(id, format, index_is_id=True) self.remove_format(id, format, index_is_id=True)
@ -578,6 +591,9 @@ class LibraryDatabase2(LibraryDatabase):
name = self.construct_file_name(id) name = self.construct_file_name(id)
ext = ('.' + format.lower()) if format else '' ext = ('.' + format.lower()) if format else ''
dest = os.path.join(path, name+ext) dest = os.path.join(path, name+ext)
pdir = os.path.dirname(dest)
if not os.path.exists(pdir):
os.makedirs(pdir)
with open(dest, 'wb') as f: with open(dest, 'wb') as f:
shutil.copyfileobj(stream, f) shutil.copyfileobj(stream, f)
stream.seek(0, 2) stream.seek(0, 2)