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.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))
|
||||||
|
@ -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,51 +23,68 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item row="1" column="0" >
|
||||||
<widget class="QSplitter" name="splitter" >
|
<widget class="QGraphicsView" name="cover" />
|
||||||
<property name="sizePolicy" >
|
</item>
|
||||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
<item row="1" column="1" >
|
||||||
<horstretch>0</horstretch>
|
<layout class="QVBoxLayout" name="verticalLayout" >
|
||||||
<verstretch>0</verstretch>
|
<item>
|
||||||
</sizepolicy>
|
<widget class="QLabel" name="text" >
|
||||||
</property>
|
<property name="text" >
|
||||||
<property name="orientation" >
|
<string>TextLabel</string>
|
||||||
<enum>Qt::Horizontal</enum>
|
</property>
|
||||||
</property>
|
<property name="alignment" >
|
||||||
<widget class="QGraphicsView" name="cover" />
|
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
||||||
<widget class="QWidget" name="" >
|
</property>
|
||||||
<layout class="QVBoxLayout" >
|
<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>
|
<item>
|
||||||
<widget class="QLabel" name="text" >
|
<widget class="QPushButton" name="previous_button" >
|
||||||
<property name="text" >
|
<property name="text" >
|
||||||
<string>TextLabel</string>
|
<string>&Previous</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="alignment" >
|
<property name="icon" >
|
||||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
|
<iconset resource="../images.qrc" >
|
||||||
</property>
|
<normaloff>:/images/previous.svg</normaloff>:/images/previous.svg</iconset>
|
||||||
<property name="wordWrap" >
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox" >
|
<widget class="QPushButton" name="next_button" >
|
||||||
<property name="title" >
|
<property name="text" >
|
||||||
<string>Comments</string>
|
<string>&Next</string>
|
||||||
|
</property>
|
||||||
|
<property name="icon" >
|
||||||
|
<iconset resource="../images.qrc" >
|
||||||
|
<normaloff>:/images/next.svg</normaloff>:/images/next.svg</iconset>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" >
|
|
||||||
<item row="0" column="0" >
|
|
||||||
<widget class="QTextBrowser" name="comments" />
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</item>
|
||||||
</widget>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<resources/>
|
<resources>
|
||||||
|
<include location="../images.qrc" />
|
||||||
|
</resources>
|
||||||
<connections/>
|
<connections/>
|
||||||
</ui>
|
</ui>
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
|
||||||
|
|
||||||
############################################################################
|
############################################################################
|
||||||
|
|
||||||
|
@ -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))
|
||||||
|
@ -418,9 +418,16 @@ 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,8 +566,19 @@ 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):
|
||||||
f = open(path, mode)
|
return path
|
||||||
return f if as_file else f.read()
|
|
||||||
|
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)
|
self.remove_format(id, format, index_is_id=True)
|
||||||
|
|
||||||
def add_format(self, index, format, stream, index_is_id=False, path=None):
|
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)
|
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user