mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
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:
parent
8c53abe905
commit
5e236b8edb
@ -5,24 +5,68 @@ __docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
'''
|
||||
import textwrap
|
||||
import textwrap, os
|
||||
|
||||
from PyQt4.QtCore import QCoreApplication
|
||||
from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon
|
||||
from PyQt4.QtCore import QCoreApplication, SIGNAL, QModelIndex, QUrl
|
||||
from PyQt4.QtGui import QDialog, QPixmap, QGraphicsScene, QIcon, QDesktopServices
|
||||
|
||||
from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
|
||||
|
||||
class BookInfo(QDialog, Ui_BookInfo):
|
||||
|
||||
def __init__(self, parent, info):
|
||||
def __init__(self, parent, view, row):
|
||||
QDialog.__init__(self, parent)
|
||||
Ui_BookInfo.__init__(self)
|
||||
self.setupUi(self)
|
||||
|
||||
self.setWindowTitle(info[_('Title')])
|
||||
desktop = QCoreApplication.instance().desktop()
|
||||
screen_height = desktop.availableGeometry().height() - 100
|
||||
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.comments.setText(info.pop(_('Comments'), ''))
|
||||
|
||||
@ -37,6 +81,15 @@ class BookInfo(QDialog, Ui_BookInfo):
|
||||
rows = u''
|
||||
self.text.setText('')
|
||||
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():
|
||||
txt = info[key]
|
||||
txt = u'<br />\n'.join(textwrap.wrap(txt, 120))
|
||||
|
@ -6,14 +6,14 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>917</width>
|
||||
<height>780</height>
|
||||
<height>783</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" >
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout" >
|
||||
<item row="0" column="0" colspan="2" >
|
||||
<widget class="QLabel" name="title" >
|
||||
<property name="text" >
|
||||
<string>TextLabel</string>
|
||||
@ -23,51 +23,68 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<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="QWidget" name="" >
|
||||
<layout class="QVBoxLayout" >
|
||||
<item row="1" column="0" >
|
||||
<widget class="QGraphicsView" name="cover" />
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="text" >
|
||||
<property name="text" >
|
||||
<string>TextLabel</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Comments</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QTextBrowser" name="comments" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QLabel" name="text" >
|
||||
<widget class="QPushButton" name="previous_button" >
|
||||
<property name="text" >
|
||||
<string>TextLabel</string>
|
||||
<string>&Previous</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||
</property>
|
||||
<property name="wordWrap" >
|
||||
<bool>true</bool>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/previous.svg</normaloff>:/images/previous.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox" >
|
||||
<property name="title" >
|
||||
<string>Comments</string>
|
||||
<widget class="QPushButton" name="next_button" >
|
||||
<property name="text" >
|
||||
<string>&Next</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../images.qrc" >
|
||||
<normaloff>:/images/next.svg</normaloff>:/images/next.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QTextBrowser" name="comments" />
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../images.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
@ -230,6 +230,7 @@ class BooksModel(QAbstractTableModel):
|
||||
else:
|
||||
formats = _('None')
|
||||
data[_('Formats')] = formats
|
||||
data[_('Path')] = self.db.abspath(idx)
|
||||
comments = self.db.comments(idx)
|
||||
if not comments:
|
||||
comments = _('None')
|
||||
@ -265,6 +266,8 @@ class BooksModel(QAbstractTableModel):
|
||||
return data
|
||||
|
||||
def get_book_info(self, index):
|
||||
if isinstance(index, int):
|
||||
index = self.index(index, 0)
|
||||
data = self.current_changed(index, None, False)
|
||||
row = index.row()
|
||||
data[_('Title')] = self.db.title(row)
|
||||
|
@ -1185,8 +1185,7 @@ in which you want to store your books files. Any existing books will be automati
|
||||
return
|
||||
index = self.library_view.currentIndex()
|
||||
if index.isValid():
|
||||
info = self.library_view.model().get_book_info(index)
|
||||
BookInfo(self, info).show()
|
||||
BookInfo(self, self.library_view, index).show()
|
||||
|
||||
############################################################################
|
||||
|
||||
|
@ -73,7 +73,7 @@ class BookInfoDisplay(QFrame):
|
||||
|
||||
rows = u''
|
||||
self.book_data.setText('')
|
||||
self.data = data
|
||||
self.data = data.copy()
|
||||
for key in data.keys():
|
||||
txt = data[key]
|
||||
#txt = '<br />\n'.join(textwrap.wrap(txt, 120))
|
||||
|
@ -418,9 +418,16 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
|
||||
def path(self, index, index_is_id=False):
|
||||
'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)
|
||||
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):
|
||||
@ -550,13 +557,8 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
return ','.join(ans)
|
||||
|
||||
|
||||
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`
|
||||
'''
|
||||
def format_abspath(self, index, format, index_is_id=False):
|
||||
'Return absolute path to the ebook file of format `format`'
|
||||
id = index if index_is_id else self.id(index)
|
||||
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]
|
||||
@ -564,8 +566,19 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
format = ('.' + format.lower()) if format else ''
|
||||
path = os.path.join(path, name+format)
|
||||
if os.access(path, os.R_OK|os.W_OK):
|
||||
f = open(path, mode)
|
||||
return f if as_file else f.read()
|
||||
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)
|
||||
return f if as_file else f.read()
|
||||
self.remove_format(id, format, index_is_id=True)
|
||||
|
||||
def add_format(self, index, format, stream, index_is_id=False, path=None):
|
||||
@ -578,6 +591,9 @@ class LibraryDatabase2(LibraryDatabase):
|
||||
name = self.construct_file_name(id)
|
||||
ext = ('.' + format.lower()) if format else ''
|
||||
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:
|
||||
shutil.copyfileobj(stream, f)
|
||||
stream.seek(0, 2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user