syn to trunk

This commit is contained in:
John Schember 2009-01-03 07:11:12 -05:00
commit b5967880b5
10 changed files with 97 additions and 53 deletions

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.4.123' __version__ = '0.4.124'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
''' '''
Various run time constants. Various run time constants.

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2009, John Schember <john at nachtimwald.com'
''' '''
''' '''
@ -10,30 +10,30 @@ from calibre.devices.interface import BookList as _BookList
EBOOK_DIR = "eBooks" EBOOK_DIR = "eBooks"
EBOOK_TYPES = ['mobi', 'prc', 'html', 'pdf', 'rtf', 'txt'] EBOOK_TYPES = ['mobi', 'prc', 'html', 'pdf', 'rtf', 'txt']
class Book(object): class Book(object):
def __init__(self, path, title, authors): def __init__(self, path, title, authors):
self.title = title self.title = title
self.authors = authors self.authors = authors
self.size = os.path.getsize(path) self.size = os.path.getsize(path)
self.datetime = time.gmtime(os.path.getctime(path)) self.datetime = time.gmtime(os.path.getctime(path))
self.path = path self.path = path
self.thumbnail = None self.thumbnail = None
self.tags = [] self.tags = []
@apply @apply
def thumbnail(): def thumbnail():
return 0 return None
def __str__(self): def __str__(self):
""" Return a utf-8 encoded string with title author and path information """ """ Return a utf-8 encoded string with title author and path information """
return self.title.encode('utf-8') + " by " + \ return self.title.encode('utf-8') + " by " + \
self.authors.encode('utf-8') + " at " + self.path.encode('utf-8') self.authors.encode('utf-8') + " at " + self.path.encode('utf-8')
class BookList(_BookList): class BookList(_BookList):
def __init__(self, mountpath): def __init__(self, mountpath):
self._mountpath = mountpath self._mountpath = mountpath
_BookList.__init__(self) _BookList.__init__(self)
self.return_books(mountpath) self.return_books(mountpath)
def return_books(self, mountpath): def return_books(self, mountpath):

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>' __copyright__ = '2009, John Schember <john at nachtimwald.com'
''' '''
Device driver for Bookeen's Cybook Gen 3 Device driver for Bookeen's Cybook Gen 3

View File

@ -309,7 +309,8 @@ class MobiReader(object):
except: except:
text = '' text = ''
text = ent_pat.sub(entity_to_unicode, text) text = ent_pat.sub(entity_to_unicode, text)
tocobj.add_item(toc.partition('#')[0], a['href'][1:], text) if a['href'].startswith('#'):
tocobj.add_item(toc.partition('#')[0], a['href'][1:], text)
if tocobj is not None: if tocobj is not None:
opf.set_toc(tocobj) opf.set_toc(tocobj)

View File

@ -7,7 +7,7 @@ add/remove formats
import os import os
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog, QCompleter
from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \ from calibre.gui2 import qstring_to_unicode, error_dialog, file_icon_provider, \
@ -33,6 +33,13 @@ class Format(QListWidgetItem):
QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext), QListWidgetItem.__init__(self, file_icon_provider().icon_from_ext(ext),
text, parent, QListWidgetItem.UserType) text, parent, QListWidgetItem.UserType)
class AuthorCompleter(QCompleter):
def __init__(self, db):
all_authors = db.all_authors()
all_authors.sort(cmp=lambda x, y : cmp(x[1], y[1]))
QCompleter.__init__(self, [x[1] for x in all_authors])
class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog): class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
def do_reset_cover(self, *args): def do_reset_cover(self, *args):
@ -171,6 +178,8 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.cover_changed = False self.cover_changed = False
self.cpixmap = None self.cpixmap = None
self.cover.setAcceptDrops(True) self.cover.setAcceptDrops(True)
self._author_completer = AuthorCompleter(self.db)
self.authors.setCompleter(self._author_completer)
self.connect(self.cover, SIGNAL('cover_changed()'), self.cover_dropped) self.connect(self.cover, SIGNAL('cover_changed()'), self.cover_dropped)
QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \ QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \
self.select_cover) self.select_cover)
@ -206,8 +215,6 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.authors.setText('') self.authors.setText('')
aus = self.db.author_sort(row) aus = self.db.author_sort(row)
self.author_sort.setText(aus if aus else '') self.author_sort.setText(aus if aus else '')
pub = self.db.publisher(row)
self.publisher.setText(pub if pub else '')
tags = self.db.tags(row) tags = self.db.tags(row)
self.tags.setText(tags if tags else '') self.tags.setText(tags if tags else '')
rating = self.db.rating(row) rating = self.db.rating(row)
@ -225,8 +232,9 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
ext = '' ext = ''
size = self.db.sizeof_format(row, ext) size = self.db.sizeof_format(row, ext)
Format(self.formats, ext, size) Format(self.formats, ext, size)
self.initialize_series() self.initialize_series_and_publisher()
self.series_index.setValue(self.db.series_index(row)) self.series_index.setValue(self.db.series_index(row))
QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index) QObject.connect(self.series, SIGNAL('currentIndexChanged(int)'), self.enable_series_index)
@ -259,7 +267,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
def cover_dropped(self): def cover_dropped(self):
self.cover_changed = True self.cover_changed = True
def initialize_series(self): def initialize_series_and_publisher(self):
all_series = self.db.all_series() all_series = self.db.all_series()
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1])) all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
series_id = self.db.series_id(self.row) series_id = self.db.series_id(self.row)
@ -282,6 +290,22 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
if l: if l:
l.invalidate() l.invalidate()
l.activate() l.activate()
all_publishers = self.db.all_publishers()
all_publishers.sort(cmp=lambda x, y : cmp(x[1], y[1]))
publisher_id = self.db.publisher_id(self.row)
idx, c = None, 0
for i in all_publishers:
id, name = i
if id == publisher_id:
idx = c
self.publisher.addItem(name)
c += 1
self.publisher.setEditText('')
if idx is not None:
self.publisher.setCurrentIndex(idx)
self.layout().activate() self.layout().activate()
@ -337,7 +361,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
isbn = qstring_to_unicode(self.isbn.text()) isbn = qstring_to_unicode(self.isbn.text())
title = qstring_to_unicode(self.title.text()) title = qstring_to_unicode(self.title.text())
author = string_to_authors(unicode(self.authors.text()))[0] author = string_to_authors(unicode(self.authors.text()))[0]
publisher = qstring_to_unicode(self.publisher.text()) publisher = qstring_to_unicode(self.publisher.currentText())
if isbn or title or author or publisher: if isbn or title or author or publisher:
d = FetchMetadata(self, isbn, title, author, publisher, self.timeout) d = FetchMetadata(self, isbn, title, author, publisher, self.timeout)
d.exec_() d.exec_()
@ -347,7 +371,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.title.setText(book.title) self.title.setText(book.title)
self.authors.setText(authors_to_string(book.authors)) self.authors.setText(authors_to_string(book.authors))
if book.author_sort: self.author_sort.setText(book.author_sort) if book.author_sort: self.author_sort.setText(book.author_sort)
if book.publisher: self.publisher.setText(book.publisher) if book.publisher: self.publisher.setEditText(book.publisher)
if book.isbn: self.isbn.setText(book.isbn) if book.isbn: self.isbn.setText(book.isbn)
summ = book.comments summ = book.comments
if summ: if summ:
@ -386,7 +410,7 @@ class MetadataSingleDialog(QDialog, Ui_MetadataSingleDialog):
self.db.set_author_sort(self.id, aus, notify=False) self.db.set_author_sort(self.id, aus, notify=False)
self.db.set_isbn(self.id, qstring_to_unicode(self.isbn.text()), notify=False) self.db.set_isbn(self.id, qstring_to_unicode(self.isbn.text()), notify=False)
self.db.set_rating(self.id, 2*self.rating.value(), notify=False) self.db.set_rating(self.id, 2*self.rating.value(), notify=False)
self.db.set_publisher(self.id, qstring_to_unicode(self.publisher.text()), notify=False) self.db.set_publisher(self.id, qstring_to_unicode(self.publisher.currentText()), notify=False)
self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(','), notify=False) self.db.set_tags(self.id, qstring_to_unicode(self.tags.text()).split(','), notify=False)
self.db.set_series(self.id, qstring_to_unicode(self.series.currentText()), notify=False) self.db.set_series(self.id, qstring_to_unicode(self.series.currentText()), notify=False)
self.db.set_series_index(self.id, self.series_index.value(), notify=False) self.db.set_series_index(self.id, self.series_index.value(), notify=False)

View File

@ -95,13 +95,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1" >
<widget class="QLineEdit" name="authors" >
<property name="toolTip" >
<string>Change the author(s) of this book. Multiple authors should be separated by an &amp;. If the author name contains an &amp;, use &amp;&amp; to represent it.</string>
</property>
</widget>
</item>
<item row="2" column="0" > <item row="2" column="0" >
<widget class="QLabel" name="label_8" > <widget class="QLabel" name="label_8" >
<property name="text" > <property name="text" >
@ -111,7 +104,7 @@
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property> </property>
<property name="buddy" > <property name="buddy" >
<cstring>authors</cstring> <cstring>author_sort</cstring>
</property> </property>
</widget> </widget>
</item> </item>
@ -185,13 +178,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="4" column="1" colspan="2" >
<widget class="QLineEdit" name="publisher" >
<property name="toolTip" >
<string>Change the publisher of this book</string>
</property>
</widget>
</item>
<item row="5" column="0" > <item row="5" column="0" >
<widget class="QLabel" name="label_4" > <widget class="QLabel" name="label_4" >
<property name="text" > <property name="text" >
@ -330,6 +316,16 @@
<item row="8" column="1" colspan="2" > <item row="8" column="1" colspan="2" >
<widget class="QLineEdit" name="isbn" /> <widget class="QLineEdit" name="isbn" />
</item> </item>
<item row="4" column="1" >
<widget class="QComboBox" name="publisher" >
<property name="editable" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="authors" />
</item>
</layout> </layout>
</widget> </widget>
</item> </item>
@ -615,8 +611,8 @@
<slot>accept()</slot> <slot>accept()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel" >
<x>257</x> <x>261</x>
<y>646</y> <y>710</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel" >
<x>157</x> <x>157</x>
@ -631,8 +627,8 @@
<slot>reject()</slot> <slot>reject()</slot>
<hints> <hints>
<hint type="sourcelabel" > <hint type="sourcelabel" >
<x>325</x> <x>329</x>
<y>646</y> <y>710</y>
</hint> </hint>
<hint type="destinationlabel" > <hint type="destinationlabel" >
<x>286</x> <x>286</x>

View File

@ -943,7 +943,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
if index_is_id: if index_is_id:
return self.conn.get('SELECT publisher FROM meta WHERE id=?', (index,), all=False) return self.conn.get('SELECT publisher FROM meta WHERE id=?', (index,), all=False)
return self.data[index][3] return self.data[index][3]
def publisher_id(self, index, index_is_id=False):
id = index if index_is_id else self.id(index)
return self.conn.get('SELECT publisher from books_publishers_link WHERE book=?', (id,), all=False)
def rating(self, index, index_is_id=False): def rating(self, index, index_is_id=False):
if index_is_id: if index_is_id:
return self.conn.get('SELECT rating FROM meta WHERE id=?', (index,), all=False) return self.conn.get('SELECT rating FROM meta WHERE id=?', (index,), all=False)
@ -1041,6 +1045,14 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
def all_series(self): def all_series(self):
return [ (i[0], i[1]) for i in \ return [ (i[0], i[1]) for i in \
self.conn.get('SELECT id, name FROM series')] self.conn.get('SELECT id, name FROM series')]
def all_authors(self):
return [ (i[0], i[1]) for i in \
self.conn.get('SELECT id, name FROM authors')]
def all_publishers(self):
return [ (i[0], i[1]) for i in \
self.conn.get('SELECT id, name FROM publishers')]
def all_tags(self): def all_tags(self):
return [i[0].strip() for i in self.conn.get('SELECT name FROM tags') if i[0].strip()] return [i[0].strip() for i in self.conn.get('SELECT name FROM tags') if i[0].strip()]

View File

@ -143,6 +143,15 @@ Where are the book files stored?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When you first run |app|, it will ask you for a folder in which to store your books. Whenever you add a book to |app|, it will copy the book into that folder. Books in the folder are nicely arranged into sub-folders by Author and Title. Metadata about the books is stored in the file ``metadata.db`` (which is a sqlite database). When you first run |app|, it will ask you for a folder in which to store your books. Whenever you add a book to |app|, it will copy the book into that folder. Books in the folder are nicely arranged into sub-folders by Author and Title. Metadata about the books is stored in the file ``metadata.db`` (which is a sqlite database).
Why doesn't |app| let me store books in my own directory structure?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The whole point if |app|'s library management features is that they provide an interface for locating books that is *much* more efficient than any possible directory scheme you could come up with for your collection. Indeed, once you become comfortable using |app|'s interface to find, sort and browse your collection, you wont ever feel the need to hunt through the files on your disk to find a book again. By managing books in its own directory struture of Author -> Title -> Book files, |app| is able to achieve a high level of reliability and standardization.
Why doesn't |app| have a column for foo?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|app| is designed to have columns for the most frequently and widely used fields. If it does not have a coulmn for your favorite field, you can always add a tag to the book for that piece of information. |app| also supports a general purpose "comments" fields for longer items.
Content From The Web Content From The Web
--------------------- ---------------------

View File

@ -133,7 +133,7 @@ to the recipe. Finally, lets replace some of the :term:`CSS` that we disabled ea
With these additions, our recipe has become "production quality", indeed it is very close to the actual recipe used by |app| for the *BBC*, shown below: With these additions, our recipe has become "production quality", indeed it is very close to the actual recipe used by |app| for the *BBC*, shown below:
.. literalinclude:: ../web/feeds/recipes/bbc.py .. literalinclude:: ../web/feeds/recipes/recipe_bbc.py
This :term:`recipe` explores only the tip of the iceberg when it comes to the power of |app|. To explore more of the abilities of |app| we'll examine a more complex real life example in the next section. This :term:`recipe` explores only the tip of the iceberg when it comes to the power of |app|. To explore more of the abilities of |app| we'll examine a more complex real life example in the next section.

View File

@ -102,6 +102,8 @@ class Stats:
def get_deviation(self, amounts): def get_deviation(self, amounts):
l = float(len(amounts)) l = float(len(amounts))
if l == 0:
return 0
mean = sum(amounts)/l mean = sum(amounts)/l
return sqrt( sum([i**2 for i in amounts])/l - mean**2 ) return sqrt( sum([i**2 for i in amounts])/l - mean**2 )