mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Update to 0.4.72 from trunk
This commit is contained in:
commit
cd1340871c
6
Makefile
6
Makefile
@ -1,8 +1,10 @@
|
||||
PYTHON = python
|
||||
|
||||
all : plugins pictureflow gui2 translations resources
|
||||
all : plugins gui2 translations resources
|
||||
|
||||
plugins:
|
||||
plugins : src/calibre/plugins pictureflow
|
||||
|
||||
src/calibre/plugins:
|
||||
mkdir -p src/calibre/plugins
|
||||
|
||||
clean :
|
||||
|
153
linux_installer.py
Normal file
153
linux_installer.py
Normal file
@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Create linux binary.
|
||||
'''
|
||||
import glob, sys, subprocess, tarfile, os, re
|
||||
HOME = '/home/kovid'
|
||||
PYINSTALLER = os.path.expanduser('~/build/pyinstaller')
|
||||
CALIBREPREFIX = '___'
|
||||
CLIT = '/usr/bin/clit'
|
||||
PDFTOHTML = '/usr/bin/pdftohtml'
|
||||
LIBUNRAR = '/usr/lib/libunrar.so'
|
||||
QTDIR = '/usr/lib/qt4'
|
||||
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml')
|
||||
EXTRAS = ('/usr/lib/python2.5/site-packages/PIL', os.path.expanduser('~/ipython/IPython'))
|
||||
|
||||
|
||||
CALIBRESRC = os.path.join(CALIBREPREFIX, 'src')
|
||||
CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins')
|
||||
|
||||
sys.path.insert(0, CALIBRESRC)
|
||||
from calibre import __version__
|
||||
|
||||
def run_pyinstaller(args=sys.argv):
|
||||
subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1]))
|
||||
subprocess.check_call('rm -rf %(py)s/dist/* %(py)s/build/*'%dict(py=PYINSTALLER), shell=True)
|
||||
subprocess.check_call('make plugins', shell=True)
|
||||
cp = HOME+'/build/'+os.path.basename(os.getcwd())
|
||||
spec = open(os.path.join(PYINSTALLER, 'calibre', 'calibre.spec'), 'wb')
|
||||
raw = re.sub(r'CALIBREPREFIX\s+=\s+\'___\'', 'CALIBREPREFIX = '+repr(cp),
|
||||
open(__file__).read())
|
||||
spec.write(raw)
|
||||
spec.close()
|
||||
os.chdir(PYINSTALLER)
|
||||
subprocess.check_call('python -OO Build.py calibre/calibre.spec', shell=True)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__' and 'linux_installer.py' in __file__:
|
||||
sys.exit(run_pyinstaller())
|
||||
|
||||
|
||||
loader = os.path.join(os.path.expanduser('~/temp'), 'calibre_installer_loader.py')
|
||||
if not os.path.exists(loader):
|
||||
open(loader, 'wb').write('''
|
||||
import sys, os
|
||||
sys.frozen_path = os.getcwd()
|
||||
os.chdir(os.environ.get("ORIGWD", "."))
|
||||
sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz"))
|
||||
sys.path.insert(0, sys.frozen_path)
|
||||
from PyQt4.QtCore import QCoreApplication
|
||||
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")])
|
||||
''')
|
||||
excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr',
|
||||
'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font',
|
||||
'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg',
|
||||
'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing',
|
||||
'TKinter', 'atk', 'gobject._gobject', 'pango', 'PIL', 'Image', 'IPython']
|
||||
temp = ['keyword', 'codeop']
|
||||
|
||||
recipes = ['calibre', 'web', 'feeds', 'recipes']
|
||||
prefix = '.'.join(recipes)+'.'
|
||||
for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))):
|
||||
temp.append(prefix + os.path.basename(f).partition('.')[0])
|
||||
hook = os.path.expanduser('~/temp/hook-calibre.py')
|
||||
f = open(hook, 'wb')
|
||||
hook_script = 'hiddenimports = %s'%repr(temp)
|
||||
f.write(hook_script)
|
||||
|
||||
sys.path.insert(0, CALIBRESRC)
|
||||
from calibre.linux import entry_points
|
||||
|
||||
executables, scripts = ['calibre_postinstall', 'parallel'], \
|
||||
[os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')]
|
||||
|
||||
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
|
||||
fields = entry.split('=')
|
||||
executables.append(fields[0].strip())
|
||||
scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py')
|
||||
|
||||
recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')),
|
||||
pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes)
|
||||
analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script],
|
||||
pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts]
|
||||
|
||||
pyz = TOC()
|
||||
binaries = TOC()
|
||||
|
||||
for a in analyses:
|
||||
pyz = a.pure + pyz
|
||||
binaries = a.binaries + binaries
|
||||
pyz = PYZ(pyz + recipes.pure, name='library.pyz')
|
||||
|
||||
built_executables = []
|
||||
for script, exe, a in zip(scripts, executables, analyses):
|
||||
built_executables.append(EXE(PYZ(TOC()),
|
||||
a.scripts+[('O','','OPTION'),],
|
||||
exclude_binaries=1,
|
||||
name=os.path.join('buildcalibre', exe),
|
||||
debug=False,
|
||||
strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
console=1))
|
||||
|
||||
print 'Adding plugins...'
|
||||
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
|
||||
binaries += [(os.path.basename(f), f, 'BINARY')]
|
||||
|
||||
print 'Adding external programs...'
|
||||
binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'),
|
||||
('libunrar.so', LIBUNRAR, 'BINARY')]
|
||||
qt = []
|
||||
for dll in QTDLLS:
|
||||
path = os.path.join(QTDIR, 'lib'+dll+'.so.4')
|
||||
qt.append((os.path.basename(path), path, 'BINARY'))
|
||||
binaries += qt
|
||||
|
||||
plugins = []
|
||||
plugdir = os.path.join(QTDIR, 'plugins')
|
||||
for dirpath, dirnames, filenames in os.walk(plugdir):
|
||||
for f in filenames:
|
||||
if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue
|
||||
f = os.path.join(dirpath, f)
|
||||
plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY'))
|
||||
binaries += plugins
|
||||
|
||||
manifest = '/tmp/manifest'
|
||||
open(manifest, 'wb').write('\\n'.join(executables))
|
||||
version = '/tmp/version'
|
||||
open(version, 'wb').write(__version__)
|
||||
coll = COLLECT(binaries, pyz,
|
||||
[('manifest', manifest, 'DATA'), ('version', version, 'DATA')],
|
||||
*built_executables,
|
||||
**dict(strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
name='dist'))
|
||||
|
||||
os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
|
||||
for folder in EXTRAS:
|
||||
subprocess.check_call('cp -rf %s .'%folder, shell=True)
|
||||
|
||||
print 'Building tarball...'
|
||||
tbz2 = 'calibre-%s-i686.tar.bz2'%__version__
|
||||
tf = tarfile.open(os.path.join('/tmp', tbz2), 'w:bz2')
|
||||
|
||||
for f in os.listdir('.'):
|
||||
tf.add(f)
|
@ -99,8 +99,7 @@ _check_symlinks_prescript()
|
||||
includes=list(self.includes) + main_modules['console'],
|
||||
packages=self.packages,
|
||||
excludes=self.excludes,
|
||||
debug=debug,
|
||||
)
|
||||
debug=debug)
|
||||
|
||||
@classmethod
|
||||
def makedmg(cls, d, volname,
|
||||
@ -249,6 +248,11 @@ _check_symlinks_prescript()
|
||||
else:
|
||||
os.link(src, os.path.join(module_dir, dest))
|
||||
print
|
||||
print 'Adding IPython'
|
||||
dst = os.path.join(resource_dir, 'lib', 'python2.5', 'IPython')
|
||||
if os.path.exists(dst): shutil.rmtree(dst)
|
||||
shutil.copytree(os.path.expanduser('~/build/ipython/IPython'), dst)
|
||||
print
|
||||
print 'Installing prescipt'
|
||||
sf = [os.path.basename(s) for s in all_names]
|
||||
cs = BuildAPP.CHECK_SYMLINKS_PRESCRIPT % dict(dest_path=repr('/usr/bin'),
|
||||
@ -277,13 +281,7 @@ sys.frameworks_dir = os.path.join(os.path.dirname(os.environ['RESOURCEPATH']), '
|
||||
|
||||
|
||||
def main():
|
||||
# auto = '--auto' in sys.argv
|
||||
# if auto:
|
||||
# sys.argv.remove('--auto')
|
||||
# if auto and not os.path.exists('dist/auto'):
|
||||
# print '%s does not exist'%os.path.abspath('dist/auto')
|
||||
# return 1
|
||||
#
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
||||
sys.argv[1:2] = ['py2app']
|
||||
setup(
|
||||
name = APPNAME,
|
||||
@ -300,12 +298,12 @@ def main():
|
||||
'PyQt4.QtSvg',
|
||||
'mechanize', 'ClientForm', 'usbobserver',
|
||||
'genshi', 'calibre.web.feeds.recipes.*',
|
||||
'IPython.Extensions.*', 'pydoc'],
|
||||
'keyword', 'codeop', 'pydoc'],
|
||||
'packages' : ['PIL', 'Authorization', 'rtf2xml', 'lxml'],
|
||||
'excludes' : [],
|
||||
'excludes' : ['IPython'],
|
||||
'plist' : { 'CFBundleGetInfoString' : '''calibre, an E-book management application.'''
|
||||
''' Visit http://calibre.kovidgoyal.net for details.''',
|
||||
'CFBundleIdentifier':'net.kovidgoyal.librs500',
|
||||
'CFBundleIdentifier':'net.kovidgoyal.calibre',
|
||||
'CFBundleShortVersionString':VERSION,
|
||||
'CFBundleVersion':APPNAME + ' ' + VERSION,
|
||||
'LSMinimumSystemVersion':'10.4.3',
|
||||
|
@ -1,7 +1,7 @@
|
||||
''' E-book management software'''
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
__version__ = '0.4.70'
|
||||
__version__ = '0.4.72'
|
||||
__docformat__ = "epytext"
|
||||
__author__ = "Kovid Goyal <kovid at kovidgoyal.net>"
|
||||
__appname__ = 'calibre'
|
||||
@ -447,14 +447,13 @@ class Settings(QSettings):
|
||||
self.setValue(str(key), QVariant(QByteArray(val)))
|
||||
|
||||
_settings = Settings()
|
||||
|
||||
if not _settings.get('rationalized'):
|
||||
__settings = Settings(name='calibre')
|
||||
dbpath = os.path.join(os.path.expanduser('~'), 'library1.db').decode(sys.getfilesystemencoding())
|
||||
dbpath = unicode(__settings.value('database path',
|
||||
QVariant(QString.fromUtf8(dbpath.encode('utf-8')))).toString())
|
||||
cmdline = __settings.value('LRF conversion defaults', QVariant(QByteArray(''))).toByteArray().data()
|
||||
|
||||
|
||||
_settings.set('database path', dbpath)
|
||||
if cmdline:
|
||||
cmdline = cPickle.loads(cmdline)
|
||||
@ -464,6 +463,7 @@ if not _settings.get('rationalized'):
|
||||
os.unlink(unicode(__settings.fileName()))
|
||||
except:
|
||||
pass
|
||||
_settings.set('database path', dbpath)
|
||||
|
||||
_spat = re.compile(r'^the\s+|^a\s+|^an\s+', re.IGNORECASE)
|
||||
def english_sort(x, y):
|
||||
|
@ -275,10 +275,10 @@ class PRS505(Device):
|
||||
if not iswindows:
|
||||
if self._main_prefix is not None:
|
||||
stats = os.statvfs(self._main_prefix)
|
||||
msz = stats.f_bsize * stats.f_bavail
|
||||
msz = stats.f_frsize * stats.f_bavail
|
||||
if self._card_prefix is not None:
|
||||
stats = os.statvfs(self._card_prefix)
|
||||
csz = stats.f_bsize * stats.f_bavail
|
||||
csz = stats.f_frsize * stats.f_bavail
|
||||
else:
|
||||
msz = self._windows_space(self._main_prefix)[1]
|
||||
csz = self._windows_space(self._card_prefix)[1]
|
||||
|
@ -12,7 +12,7 @@ from PyQt4.QtGui import QDialog, QItemSelectionModel
|
||||
|
||||
from calibre.gui2.dialogs.fetch_metadata_ui import Ui_FetchMetadata
|
||||
from calibre.gui2 import error_dialog, NONE, info_dialog
|
||||
from calibre.ebooks.metadata.isbndb import create_books, option_parser
|
||||
from calibre.ebooks.metadata.isbndb import create_books, option_parser, ISBNDBError
|
||||
from calibre import Settings
|
||||
|
||||
class Matches(QAbstractTableModel):
|
||||
@ -88,8 +88,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
||||
self.connect(self.matches, SIGNAL('activated(QModelIndex)'), self.chosen)
|
||||
key = str(self.key.text())
|
||||
if key:
|
||||
QTimer.singleShot(100, self.fetch.click)
|
||||
|
||||
QTimer.singleShot(100, self.fetch_metadata)
|
||||
|
||||
|
||||
def show_summary(self, current, previous):
|
||||
@ -106,7 +105,7 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
||||
_('You must specify a valid access key for isbndb.com'))
|
||||
return
|
||||
else:
|
||||
Settings().set('isbndb.com key', str(self.key.text()))
|
||||
Settings().set('isbndb.com key', key)
|
||||
|
||||
args = ['isbndb']
|
||||
if self.isbn:
|
||||
@ -121,36 +120,41 @@ class FetchMetadata(QDialog, Ui_FetchMetadata):
|
||||
self.fetch.setEnabled(False)
|
||||
self.setCursor(Qt.WaitCursor)
|
||||
QCoreApplication.instance().processEvents()
|
||||
|
||||
args.append(key)
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
|
||||
self.logger = logging.getLogger('Job #'+str(id))
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
self.log_dest = cStringIO.StringIO()
|
||||
handler = logging.StreamHandler(self.log_dest)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
|
||||
self.logger.addHandler(handler)
|
||||
|
||||
books = create_books(opts, args, self.logger, self.timeout)
|
||||
|
||||
self.model = Matches(books)
|
||||
if self.model.rowCount() < 1:
|
||||
info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_()
|
||||
self.reject()
|
||||
|
||||
self.matches.setModel(self.model)
|
||||
QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||
self.show_summary)
|
||||
self.model.reset()
|
||||
self.matches.selectionModel().select(self.model.index(0, 0),
|
||||
QItemSelectionModel.Select | QItemSelectionModel.Rows)
|
||||
self.matches.setCurrentIndex(self.model.index(0, 0))
|
||||
self.fetch.setEnabled(True)
|
||||
self.unsetCursor()
|
||||
self.matches.resizeColumnsToContents()
|
||||
try:
|
||||
args.append(key)
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
|
||||
self.logger = logging.getLogger('Job #'+str(id))
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
self.log_dest = cStringIO.StringIO()
|
||||
handler = logging.StreamHandler(self.log_dest)
|
||||
handler.setLevel(logging.DEBUG)
|
||||
handler.setFormatter(logging.Formatter('[%(levelname)s] %(filename)s:%(lineno)s: %(message)s'))
|
||||
self.logger.addHandler(handler)
|
||||
|
||||
try:
|
||||
books = create_books(opts, args, self.logger, self.timeout)
|
||||
except ISBNDBError, err:
|
||||
error_dialog(self, _('Error fetching metadata'), str(err)).exec_()
|
||||
return
|
||||
|
||||
self.model = Matches(books)
|
||||
if self.model.rowCount() < 1:
|
||||
info_dialog(self, _('No metadata found'), _('No metadata found, try adjusting the title and author or the ISBN key.')).exec_()
|
||||
self.reject()
|
||||
|
||||
self.matches.setModel(self.model)
|
||||
QObject.connect(self.matches.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||
self.show_summary)
|
||||
self.model.reset()
|
||||
self.matches.selectionModel().select(self.model.index(0, 0),
|
||||
QItemSelectionModel.Select | QItemSelectionModel.Rows)
|
||||
self.matches.setCurrentIndex(self.model.index(0, 0))
|
||||
finally:
|
||||
self.fetch.setEnabled(True)
|
||||
self.unsetCursor()
|
||||
self.matches.resizeColumnsToContents()
|
||||
|
||||
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>&Access Key;</string>
|
||||
<string>&Access Key:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>key</cstring>
|
||||
|
@ -6,7 +6,7 @@ add/remove formats
|
||||
'''
|
||||
import os
|
||||
|
||||
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt, QVariant
|
||||
from PyQt4.QtCore import SIGNAL, QObject, QCoreApplication, Qt
|
||||
from PyQt4.QtGui import QPixmap, QListWidgetItem, QErrorMessage, QDialog
|
||||
|
||||
|
||||
|
@ -8,8 +8,8 @@ from math import cos, sin, pi
|
||||
from itertools import repeat
|
||||
from PyQt4.QtGui import QTableView, QProgressDialog, QAbstractItemView, QColor, \
|
||||
QItemDelegate, QPainterPath, QLinearGradient, QBrush, \
|
||||
QPen, QStyle, QPainter, QLineEdit, QApplication, \
|
||||
QPalette, QImage
|
||||
QPen, QStyle, QPainter, QLineEdit, \
|
||||
QPalette, QImage, QApplication
|
||||
from PyQt4.QtCore import QAbstractTableModel, QVariant, Qt, QString, \
|
||||
QCoreApplication, SIGNAL, QObject, QSize, QModelIndex
|
||||
|
||||
@ -22,7 +22,7 @@ class LibraryDelegate(QItemDelegate):
|
||||
COLOR = QColor("blue")
|
||||
SIZE = 16
|
||||
PEN = QPen(COLOR, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
|
||||
|
||||
|
||||
def __init__(self, parent):
|
||||
QItemDelegate.__init__(self, parent)
|
||||
self.star_path = QPainterPath()
|
||||
@ -41,10 +41,10 @@ class LibraryDelegate(QItemDelegate):
|
||||
def sizeHint(self, option, index):
|
||||
#num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||
return QSize(5*(self.SIZE), self.SIZE+4)
|
||||
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
num = index.model().data(index, Qt.DisplayRole).toInt()[0]
|
||||
def draw_star():
|
||||
def draw_star():
|
||||
painter.save()
|
||||
painter.scale(self.factor, self.factor)
|
||||
painter.translate(50.0, 50.0)
|
||||
@ -52,16 +52,18 @@ class LibraryDelegate(QItemDelegate):
|
||||
painter.translate(-50.0, -50.0)
|
||||
painter.drawPath(self.star_path)
|
||||
painter.restore()
|
||||
|
||||
|
||||
painter.save()
|
||||
if hasattr(QStyle, 'CE_ItemViewItem'):
|
||||
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
|
||||
elif option.state & QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
try:
|
||||
if option.state & QStyle.State_Selected:
|
||||
painter.fillRect(option.rect, option.palette.highlight())
|
||||
painter.setRenderHint(QPainter.Antialiasing)
|
||||
y = option.rect.center().y()-self.SIZE/2.
|
||||
y = option.rect.center().y()-self.SIZE/2.
|
||||
x = option.rect.right() - self.SIZE
|
||||
painter.setPen(self.PEN)
|
||||
painter.setBrush(self.brush)
|
||||
painter.setPen(self.PEN)
|
||||
painter.setBrush(self.brush)
|
||||
painter.translate(x, y)
|
||||
i = 0
|
||||
while i < num:
|
||||
@ -71,7 +73,7 @@ class LibraryDelegate(QItemDelegate):
|
||||
except Exception, e:
|
||||
traceback.print_exc(e)
|
||||
painter.restore()
|
||||
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
sb = QItemDelegate.createEditor(self, parent, option, index)
|
||||
sb.setMinimum(0)
|
||||
@ -105,22 +107,22 @@ class BooksModel(QAbstractTableModel):
|
||||
self.read_config()
|
||||
self.buffer_size = buffer
|
||||
self.cover_cache = None
|
||||
|
||||
|
||||
def clear_caches(self):
|
||||
if self.cover_cache:
|
||||
self.cover_cache.clear_cache()
|
||||
|
||||
|
||||
def read_config(self):
|
||||
self.use_roman_numbers = Settings().get('use roman numerals for series number', True)
|
||||
|
||||
|
||||
|
||||
|
||||
def set_database(self, db):
|
||||
if isinstance(db, (QString, basestring)):
|
||||
if isinstance(db, QString):
|
||||
db = qstring_to_unicode(db)
|
||||
db = LibraryDatabase(os.path.expanduser(db))
|
||||
self.db = db
|
||||
|
||||
|
||||
def refresh_ids(self, ids, current_row=-1):
|
||||
rows = self.db.refresh_ids(ids)
|
||||
for row in rows:
|
||||
@ -132,24 +134,24 @@ class BooksModel(QAbstractTableModel):
|
||||
self.get_book_display_info(row))
|
||||
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'),
|
||||
self.index(row, 0), self.index(row, self.columnCount(None)-1))
|
||||
|
||||
|
||||
def close(self):
|
||||
self.db.close()
|
||||
self.db = None
|
||||
self.reset()
|
||||
|
||||
|
||||
def add_books(self, paths, formats, metadata, uris=[], add_duplicates=False):
|
||||
return self.db.add_books(paths, formats, metadata, uris,
|
||||
add_duplicates=add_duplicates)
|
||||
|
||||
|
||||
def row_indices(self, index):
|
||||
''' Return list indices of all cells in index.row()'''
|
||||
return [ self.index(index.row(), c) for c in range(self.columnCount(None))]
|
||||
|
||||
|
||||
def save_to_disk(self, rows, path, single_dir=False):
|
||||
rows = [row.row() for row in rows]
|
||||
self.db.export_to_dir(path, rows, self.sorted_on[0] == 1, single_dir=single_dir)
|
||||
|
||||
|
||||
def delete_books(self, indices):
|
||||
ids = [ self.id(i) for i in indices ]
|
||||
for id in ids:
|
||||
@ -159,10 +161,10 @@ class BooksModel(QAbstractTableModel):
|
||||
self.endRemoveRows()
|
||||
self.clear_caches()
|
||||
self.reset()
|
||||
|
||||
|
||||
def search_tokens(self, text):
|
||||
return text_to_tokens(text)
|
||||
|
||||
|
||||
def search(self, text, refinement, reset=True):
|
||||
tokens, OR = self.search_tokens(text)
|
||||
self.db.filter(tokens, refilter=refinement, OR=OR)
|
||||
@ -170,7 +172,7 @@ class BooksModel(QAbstractTableModel):
|
||||
if reset:
|
||||
self.clear_caches()
|
||||
self.reset()
|
||||
|
||||
|
||||
def sort(self, col, order, reset=True):
|
||||
if not self.db:
|
||||
return
|
||||
@ -179,30 +181,30 @@ class BooksModel(QAbstractTableModel):
|
||||
self.research()
|
||||
if reset:
|
||||
self.clear_caches()
|
||||
self.reset()
|
||||
self.reset()
|
||||
self.sorted_on = (col, order)
|
||||
|
||||
|
||||
def resort(self, reset=True):
|
||||
self.sort(*self.sorted_on, **dict(reset=reset))
|
||||
|
||||
|
||||
def research(self, reset=True):
|
||||
self.search(self.last_search, False, reset=reset)
|
||||
|
||||
|
||||
def database_needs_migration(self):
|
||||
path = os.path.expanduser('~/library.db')
|
||||
return self.db.is_empty() and \
|
||||
os.path.exists(path) and\
|
||||
LibraryDatabase.sizeof_old_database(path) > 0
|
||||
|
||||
|
||||
def columnCount(self, parent):
|
||||
return len(self.cols)
|
||||
|
||||
|
||||
def rowCount(self, parent):
|
||||
return self.db.rows() if self.db else 0
|
||||
|
||||
|
||||
def count(self):
|
||||
return self.rowCount(None)
|
||||
|
||||
|
||||
def get_book_display_info(self, idx):
|
||||
data = {}
|
||||
cdata = self.cover(idx)
|
||||
@ -229,9 +231,9 @@ class BooksModel(QAbstractTableModel):
|
||||
sidx = self.db.series_index(idx)
|
||||
sidx = self.__class__.roman(sidx) if self.use_roman_numbers else str(sidx)
|
||||
data[_('Series')] = _('Book <font face="serif">%s</font> of %s.')%(sidx, series)
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def set_cache(self, idx):
|
||||
l, r = 0, self.count()-1
|
||||
if self.cover_cache:
|
||||
@ -244,7 +246,7 @@ class BooksModel(QAbstractTableModel):
|
||||
ids = ids + [i for i in range(l, r, 1) if i not in ids]
|
||||
ids = [self.db.id(i) for i in ids]
|
||||
self.cover_cache.set_cache(ids)
|
||||
|
||||
|
||||
def current_changed(self, current, previous, emit_signal=True):
|
||||
idx = current.row()
|
||||
self.set_cache(idx)
|
||||
@ -253,7 +255,7 @@ class BooksModel(QAbstractTableModel):
|
||||
self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data)
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
def get_book_info(self, index):
|
||||
data = self.current_changed(index, None, False)
|
||||
row = index.row()
|
||||
@ -264,8 +266,8 @@ class BooksModel(QAbstractTableModel):
|
||||
au = ', '.join([a.strip() for a in au.split(',')])
|
||||
data[_('Author(s)')] = au
|
||||
return data
|
||||
|
||||
|
||||
|
||||
|
||||
def get_metadata(self, rows):
|
||||
metadata = []
|
||||
for row in rows:
|
||||
@ -296,11 +298,11 @@ class BooksModel(QAbstractTableModel):
|
||||
'comments': self.db.comments(row),
|
||||
}
|
||||
if series is not None:
|
||||
mi['tag order'] = {series:self.db.books_in_series_of(row)}
|
||||
|
||||
mi['tag order'] = {series:self.db.books_in_series_of(row)}
|
||||
|
||||
metadata.append(mi)
|
||||
return metadata
|
||||
|
||||
|
||||
def get_preferred_formats(self, rows, formats):
|
||||
ans = []
|
||||
for row in (row.row() for row in rows):
|
||||
@ -313,17 +315,17 @@ class BooksModel(QAbstractTableModel):
|
||||
pt = PersistentTemporaryFile(suffix='.'+format)
|
||||
pt.write(self.db.format(row, format))
|
||||
pt.seek(0)
|
||||
ans.append(pt)
|
||||
ans.append(pt)
|
||||
else:
|
||||
ans.append(None)
|
||||
return ans
|
||||
|
||||
|
||||
def id(self, row):
|
||||
return self.db.id(row.row())
|
||||
|
||||
|
||||
def title(self, row_number):
|
||||
return self.db.title(row_number)
|
||||
|
||||
|
||||
def cover(self, row_number):
|
||||
id = self.db.id(row_number)
|
||||
data = None
|
||||
@ -342,15 +344,15 @@ class BooksModel(QAbstractTableModel):
|
||||
if img.isNull():
|
||||
img = self.default_image
|
||||
return img
|
||||
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
row, col = index.row(), index.column()
|
||||
if col == 0:
|
||||
text = self.db.title(row)
|
||||
if text:
|
||||
return QVariant(text)
|
||||
elif col == 1:
|
||||
elif col == 1:
|
||||
au = self.db.authors(row)
|
||||
if au:
|
||||
au = au.split(',')
|
||||
@ -364,18 +366,18 @@ class BooksModel(QAbstractTableModel):
|
||||
if dt:
|
||||
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
||||
return QVariant(dt.strftime(BooksView.TIME_FMT).decode(preferred_encoding, 'replace'))
|
||||
elif col == 4:
|
||||
elif col == 4:
|
||||
r = self.db.rating(row)
|
||||
r = r/2 if r else 0
|
||||
return QVariant(r)
|
||||
elif col == 5:
|
||||
elif col == 5:
|
||||
pub = self.db.publisher(row)
|
||||
if pub:
|
||||
if pub:
|
||||
return QVariant(pub)
|
||||
elif col == 6:
|
||||
tags = self.db.tags(row)
|
||||
if tags:
|
||||
return QVariant(', '.join(tags.split(',')))
|
||||
return QVariant(', '.join(tags.split(',')))
|
||||
elif col == 7:
|
||||
series = self.db.series(row)
|
||||
if series:
|
||||
@ -383,16 +385,16 @@ class BooksModel(QAbstractTableModel):
|
||||
return NONE
|
||||
elif role == Qt.TextAlignmentRole and index.column() in [2, 3, 4]:
|
||||
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
|
||||
elif role == Qt.ToolTipRole and index.isValid():
|
||||
elif role == Qt.ToolTipRole and index.isValid():
|
||||
if index.column() in self.editable_cols:
|
||||
return QVariant(_("Double click to <b>edit</b> me<br><br>"))
|
||||
return NONE
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role != Qt.DisplayRole:
|
||||
return NONE
|
||||
text = ""
|
||||
if orientation == Qt.Horizontal:
|
||||
if orientation == Qt.Horizontal:
|
||||
if section == 0: text = _("Title")
|
||||
elif section == 1: text = _("Author(s)")
|
||||
elif section == 2: text = _("Size (MB)")
|
||||
@ -402,16 +404,16 @@ class BooksModel(QAbstractTableModel):
|
||||
elif section == 6: text = _("Tags")
|
||||
elif section == 7: text = _("Series")
|
||||
return QVariant(text)
|
||||
else:
|
||||
else:
|
||||
return QVariant(section+1)
|
||||
|
||||
|
||||
def flags(self, index):
|
||||
flags = QAbstractTableModel.flags(self, index)
|
||||
if index.isValid():
|
||||
if index.column() in self.editable_cols:
|
||||
flags |= Qt.ItemIsEditable
|
||||
if index.isValid():
|
||||
if index.column() in self.editable_cols:
|
||||
flags |= Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
|
||||
def setData(self, index, value, role):
|
||||
done = False
|
||||
if role == Qt.EditRole:
|
||||
@ -424,7 +426,7 @@ class BooksModel(QAbstractTableModel):
|
||||
val = 0 if val < 0 else 5 if val > 5 else val
|
||||
val *= 2
|
||||
column = self.cols[col]
|
||||
self.db.set(row, column, val)
|
||||
self.db.set(row, column, val)
|
||||
self.emit(SIGNAL("dataChanged(QModelIndex, QModelIndex)"), \
|
||||
index, index)
|
||||
if col == self.sorted_on[0]:
|
||||
@ -435,17 +437,17 @@ class BooksModel(QAbstractTableModel):
|
||||
class BooksView(TableView):
|
||||
TIME_FMT = '%d %b %Y'
|
||||
wrapper = textwrap.TextWrapper(width=20)
|
||||
|
||||
|
||||
@classmethod
|
||||
def wrap(cls, s, width=20):
|
||||
def wrap(cls, s, width=20):
|
||||
cls.wrapper.width = width
|
||||
return cls.wrapper.fill(s)
|
||||
|
||||
|
||||
@classmethod
|
||||
def human_readable(cls, size, precision=1):
|
||||
""" Convert a size in bytes into megabytes """
|
||||
return ('%.'+str(precision)+'f') % ((size/(1024.*1024.)),)
|
||||
|
||||
|
||||
def __init__(self, parent, modelcls=BooksModel):
|
||||
TableView.__init__(self, parent)
|
||||
self.display_parent = parent
|
||||
@ -454,7 +456,7 @@ class BooksView(TableView):
|
||||
self.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.setSortingEnabled(True)
|
||||
if self.__class__.__name__ == 'BooksView': # Subclasses may not have rating as col 4
|
||||
self.setItemDelegateForColumn(4, LibraryDelegate(self))
|
||||
self.setItemDelegateForColumn(4, LibraryDelegate(self))
|
||||
QObject.connect(self.selectionModel(), SIGNAL('currentRowChanged(QModelIndex, QModelIndex)'),
|
||||
self._model.current_changed)
|
||||
# Adding and removing rows should resize rows to contents
|
||||
@ -463,42 +465,42 @@ class BooksView(TableView):
|
||||
# Resetting the model should resize rows (model is reset after search and sort operations)
|
||||
QObject.connect(self.model(), SIGNAL('modelReset()'), self.resizeRowsToContents)
|
||||
self.set_visible_columns()
|
||||
|
||||
|
||||
@classmethod
|
||||
def paths_from_event(cls, event):
|
||||
'''
|
||||
Accept a drop event and return a list of paths that can be read from
|
||||
'''
|
||||
Accept a drop event and return a list of paths that can be read from
|
||||
and represent files with extensions.
|
||||
'''
|
||||
if event.mimeData().hasFormat('text/uri-list'):
|
||||
urls = [qstring_to_unicode(u.toLocalFile()) for u in event.mimeData().urls()]
|
||||
return [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)]
|
||||
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if int(event.possibleActions() & Qt.CopyAction) + \
|
||||
int(event.possibleActions() & Qt.MoveAction) == 0:
|
||||
return
|
||||
paths = self.paths_from_event(event)
|
||||
|
||||
|
||||
if paths:
|
||||
event.acceptProposedAction()
|
||||
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
event.acceptProposedAction()
|
||||
|
||||
|
||||
def dropEvent(self, event):
|
||||
paths = self.paths_from_event(event)
|
||||
event.setDropAction(Qt.CopyAction)
|
||||
event.accept()
|
||||
self.emit(SIGNAL('files_dropped(PyQt_PyObject)'), paths, Qt.QueuedConnection)
|
||||
|
||||
|
||||
self.emit(SIGNAL('files_dropped(PyQt_PyObject)'), paths, Qt.QueuedConnection)
|
||||
|
||||
|
||||
def set_database(self, db):
|
||||
self._model.set_database(db)
|
||||
|
||||
|
||||
def close(self):
|
||||
self._model.close()
|
||||
|
||||
|
||||
def migrate_database(self):
|
||||
if self.model().database_needs_migration():
|
||||
print 'Migrating database from pre 0.4.0 version'
|
||||
@ -510,39 +512,39 @@ class BooksView(TableView):
|
||||
progress.setModal(True)
|
||||
progress.setValue(0)
|
||||
app = QCoreApplication.instance()
|
||||
|
||||
|
||||
def meter(count):
|
||||
progress.setValue(count)
|
||||
app.processEvents()
|
||||
progress.setWindowTitle('Upgrading database')
|
||||
progress.show()
|
||||
LibraryDatabase.import_old_database(path, self._model.db.conn, meter)
|
||||
|
||||
|
||||
def connect_to_search_box(self, sb):
|
||||
QObject.connect(sb, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
|
||||
QObject.connect(sb, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'),
|
||||
self._model.search)
|
||||
|
||||
|
||||
def connect_to_book_display(self, bd):
|
||||
QObject.connect(self._model, SIGNAL('new_bookdisplay_data(PyQt_PyObject)'),
|
||||
bd)
|
||||
|
||||
|
||||
|
||||
class DeviceBooksView(BooksView):
|
||||
|
||||
|
||||
def __init__(self, parent):
|
||||
BooksView.__init__(self, parent, DeviceBooksModel)
|
||||
self.columns_resized = False
|
||||
self.resize_on_select = False
|
||||
|
||||
|
||||
def resizeColumnsToContents(self):
|
||||
QTableView.resizeColumnsToContents(self)
|
||||
self.columns_resized = True
|
||||
|
||||
|
||||
def connect_dirtied_signal(self, slot):
|
||||
QObject.connect(self._model, SIGNAL('booklist_dirtied()'), slot)
|
||||
|
||||
class DeviceBooksModel(BooksModel):
|
||||
|
||||
|
||||
def __init__(self, parent):
|
||||
BooksModel.__init__(self, parent)
|
||||
self.db = []
|
||||
@ -550,15 +552,15 @@ class DeviceBooksModel(BooksModel):
|
||||
self.sorted_map = []
|
||||
self.unknown = str(self.trUtf8('Unknown'))
|
||||
self.marked_for_deletion = {}
|
||||
|
||||
|
||||
|
||||
|
||||
def mark_for_deletion(self, id, rows):
|
||||
self.marked_for_deletion[id] = self.indices(rows)
|
||||
for row in rows:
|
||||
indices = self.row_indices(row)
|
||||
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1])
|
||||
|
||||
|
||||
|
||||
|
||||
def deletion_done(self, id, succeeded=True):
|
||||
if not self.marked_for_deletion.has_key(id):
|
||||
return
|
||||
@ -566,29 +568,29 @@ class DeviceBooksModel(BooksModel):
|
||||
for row in rows:
|
||||
if not succeeded:
|
||||
indices = self.row_indices(self.index(row, 0))
|
||||
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1])
|
||||
|
||||
self.emit(SIGNAL('dataChanged(QModelIndex, QModelIndex)'), indices[0], indices[-1])
|
||||
|
||||
def paths_deleted(self, paths):
|
||||
self.map = list(range(0, len(self.db)))
|
||||
self.resort(False)
|
||||
self.research(True)
|
||||
|
||||
|
||||
def indices_to_be_deleted(self):
|
||||
ans = []
|
||||
for v in self.marked_for_deletion.values():
|
||||
ans.extend(v)
|
||||
return ans
|
||||
|
||||
|
||||
def flags(self, index):
|
||||
if self.map[index.row()] in self.indices_to_be_deleted():
|
||||
return Qt.ItemIsUserCheckable # Can't figure out how to get the disabled flag in python
|
||||
flags = QAbstractTableModel.flags(self, index)
|
||||
if index.isValid():
|
||||
if index.column() in [0, 1] or (index.column() == 4 and self.db.supports_tags()):
|
||||
flags |= Qt.ItemIsEditable
|
||||
if index.isValid():
|
||||
if index.column() in [0, 1] or (index.column() == 4 and self.db.supports_tags()):
|
||||
flags |= Qt.ItemIsEditable
|
||||
return flags
|
||||
|
||||
|
||||
|
||||
|
||||
def search(self, text, refinement, reset=True):
|
||||
tokens, OR = self.search_tokens(text)
|
||||
base = self.map if refinement else self.sorted_map
|
||||
@ -611,13 +613,13 @@ class DeviceBooksModel(BooksModel):
|
||||
break
|
||||
if add:
|
||||
result.append(i)
|
||||
|
||||
|
||||
self.map = result
|
||||
|
||||
if reset:
|
||||
self.reset()
|
||||
self.last_search = text
|
||||
|
||||
|
||||
def sort(self, col, order, reset=True):
|
||||
descending = order != Qt.AscendingOrder
|
||||
def strcmp(attr):
|
||||
@ -632,7 +634,7 @@ class DeviceBooksModel(BooksModel):
|
||||
x, y = x.strip().lower(), y.strip().lower()
|
||||
return cmp(x, y)
|
||||
return _strcmp
|
||||
def datecmp(x, y):
|
||||
def datecmp(x, y):
|
||||
x = self.db[x].datetime
|
||||
y = self.db[y].datetime
|
||||
return cmp(datetime(*x[0:6]), datetime(*y[0:6]))
|
||||
@ -643,7 +645,7 @@ class DeviceBooksModel(BooksModel):
|
||||
x, y = ','.join(self.db[x].tags), ','.join(self.db[y].tags)
|
||||
return cmp(x, y)
|
||||
fcmp = strcmp('title_sorter') if col == 0 else strcmp('authors') if col == 1 else \
|
||||
sizecmp if col == 2 else datecmp if col == 3 else tagscmp
|
||||
sizecmp if col == 2 else datecmp if col == 3 else tagscmp
|
||||
self.map.sort(cmp=fcmp, reverse=descending)
|
||||
if len(self.map) == len(self.db):
|
||||
self.sorted_map = list(self.map)
|
||||
@ -653,17 +655,17 @@ class DeviceBooksModel(BooksModel):
|
||||
self.sorted_on = (col, order)
|
||||
if reset:
|
||||
self.reset()
|
||||
|
||||
|
||||
def columnCount(self, parent):
|
||||
return 5
|
||||
|
||||
|
||||
def rowCount(self, parent):
|
||||
return len(self.map)
|
||||
|
||||
|
||||
def set_database(self, db):
|
||||
self.db = db
|
||||
self.map = list(range(0, len(db)))
|
||||
|
||||
|
||||
def current_changed(self, current, previous):
|
||||
data = {}
|
||||
item = self.db[self.map[current.row()]]
|
||||
@ -686,26 +688,26 @@ class DeviceBooksModel(BooksModel):
|
||||
data[_('Timestamp')] = dt.ctime()
|
||||
data[_('Tags')] = ', '.join(item.tags)
|
||||
self.emit(SIGNAL('new_bookdisplay_data(PyQt_PyObject)'), data)
|
||||
|
||||
|
||||
def paths(self, rows):
|
||||
return [self.db[self.map[r.row()]].path for r in rows ]
|
||||
|
||||
|
||||
def indices(self, rows):
|
||||
'''
|
||||
Return indices into underlying database from rows
|
||||
'''
|
||||
return [ self.map[r.row()] for r in rows]
|
||||
|
||||
|
||||
|
||||
|
||||
def data(self, index, role):
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole:
|
||||
row, col = index.row(), index.column()
|
||||
if col == 0:
|
||||
text = self.db[self.map[row]].title
|
||||
if not text:
|
||||
text = self.unknown
|
||||
return QVariant(text)
|
||||
elif col == 1:
|
||||
elif col == 1:
|
||||
au = self.db[self.map[row]].authors
|
||||
if not au:
|
||||
au = self.unknown
|
||||
@ -726,40 +728,40 @@ class DeviceBooksModel(BooksModel):
|
||||
dt = dt - timedelta(seconds=time.timezone) + timedelta(hours=time.daylight)
|
||||
return QVariant(dt.strftime(BooksView.TIME_FMT))
|
||||
elif col == 4:
|
||||
tags = self.db[self.map[row]].tags
|
||||
tags = self.db[self.map[row]].tags
|
||||
if tags:
|
||||
return QVariant(', '.join(tags))
|
||||
return QVariant(', '.join(tags))
|
||||
elif role == Qt.TextAlignmentRole and index.column() in [2, 3]:
|
||||
return QVariant(Qt.AlignRight | Qt.AlignVCenter)
|
||||
elif role == Qt.ToolTipRole and index.isValid():
|
||||
if self.map[index.row()] in self.indices_to_be_deleted():
|
||||
return QVariant('Marked for deletion')
|
||||
return QVariant('Marked for deletion')
|
||||
col = index.column()
|
||||
if col in [0, 1] or (col == 4 and self.db.supports_tags()):
|
||||
return QVariant("Double click to <b>edit</b> me<br><br>")
|
||||
return NONE
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
if role != Qt.DisplayRole:
|
||||
return NONE
|
||||
text = ""
|
||||
if orientation == Qt.Horizontal:
|
||||
if orientation == Qt.Horizontal:
|
||||
if section == 0: text = _("Title")
|
||||
elif section == 1: text = _("Author(s)")
|
||||
elif section == 2: text = _("Size (MB)")
|
||||
elif section == 3: text = _("Date")
|
||||
elif section == 4: text = _("Tags")
|
||||
return QVariant(text)
|
||||
else:
|
||||
else:
|
||||
return QVariant(section+1)
|
||||
|
||||
|
||||
def setData(self, index, value, role):
|
||||
done = False
|
||||
if role == Qt.EditRole:
|
||||
row, col = index.row(), index.column()
|
||||
if col in [2, 3]:
|
||||
return False
|
||||
val = qstring_to_unicode(value.toString()).strip()
|
||||
val = qstring_to_unicode(value.toString()).strip()
|
||||
idx = self.map[row]
|
||||
if col == 0:
|
||||
self.db[idx].title = val
|
||||
@ -778,9 +780,9 @@ class DeviceBooksModel(BooksModel):
|
||||
return done
|
||||
|
||||
class SearchBox(QLineEdit):
|
||||
|
||||
|
||||
INTERVAL = 1000 #: Time to wait before emitting search signal
|
||||
|
||||
|
||||
def __init__(self, parent):
|
||||
QLineEdit.__init__(self, parent)
|
||||
self.help_text = _('Search (For Advanced Search click the button to the left)')
|
||||
@ -792,40 +794,40 @@ class SearchBox(QLineEdit):
|
||||
self.timer = None
|
||||
self.clear_to_help()
|
||||
QObject.connect(self, SIGNAL('textEdited(QString)'), self.text_edited_slot)
|
||||
|
||||
|
||||
|
||||
|
||||
def normalize_state(self):
|
||||
self.setText('')
|
||||
self.setPalette(self.default_palette)
|
||||
|
||||
|
||||
def clear_to_help(self):
|
||||
self.setPalette(self.gray)
|
||||
self.setText(self.help_text)
|
||||
self.home(False)
|
||||
self.home(False)
|
||||
self.initial_state = True
|
||||
|
||||
|
||||
def clear(self):
|
||||
self.clear_to_help()
|
||||
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), '', False)
|
||||
|
||||
|
||||
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if self.initial_state:
|
||||
self.normalize_state()
|
||||
self.initial_state = False
|
||||
QLineEdit.keyPressEvent(self, event)
|
||||
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.initial_state:
|
||||
self.normalize_state()
|
||||
self.initial_state = False
|
||||
QLineEdit.mouseReleaseEvent(self, event)
|
||||
|
||||
|
||||
def text_edited_slot(self, text):
|
||||
text = qstring_to_unicode(text) if isinstance(text, QString) else unicode(text)
|
||||
self.prev_text = text
|
||||
self.timer = self.startTimer(self.__class__.INTERVAL)
|
||||
|
||||
|
||||
def timerEvent(self, event):
|
||||
self.killTimer(event.timerId())
|
||||
if event.timerId() == self.timer:
|
||||
@ -833,7 +835,7 @@ class SearchBox(QLineEdit):
|
||||
refinement = text.startswith(self.prev_search) and ':' not in text
|
||||
self.prev_search = text
|
||||
self.emit(SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), text, refinement)
|
||||
|
||||
|
||||
def set_search_string(self, txt):
|
||||
self.normalize_state()
|
||||
self.setText(txt)
|
||||
|
@ -4,7 +4,7 @@ import os, sys, textwrap, collections, traceback, time
|
||||
from xml.parsers.expat import ExpatError
|
||||
from functools import partial
|
||||
from PyQt4.QtCore import Qt, SIGNAL, QObject, QCoreApplication, \
|
||||
QVariant, QThread, QSize, QUrl
|
||||
QVariant, QThread, QUrl, QSize
|
||||
from PyQt4.QtGui import QPixmap, QColor, QPainter, QMenu, QIcon, QMessageBox, \
|
||||
QToolButton, QDialog, QDesktopServices
|
||||
from PyQt4.QtSvg import QSvgRenderer
|
||||
@ -1210,4 +1210,14 @@ def main(args=sys.argv):
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
try:
|
||||
sys.exit(main())
|
||||
except:
|
||||
if not iswindows: raise
|
||||
from PyQt4.QtGui import QErrorMessage
|
||||
logfile = os.path.expanduser('~/calibre.log')
|
||||
if os.path.exists(logfile):
|
||||
log = open(logfile).read()
|
||||
if log.strip():
|
||||
d = QErrorMessage()
|
||||
d.showMessage(log)
|
||||
|
@ -27,9 +27,9 @@
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>86</y>
|
||||
<y>79</y>
|
||||
<width>865</width>
|
||||
<height>712</height>
|
||||
<height>716</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
@ -44,7 +44,7 @@
|
||||
<item>
|
||||
<widget class="LocationView" name="location_view" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Minimum" hsizetype="Expanding" >
|
||||
<sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
@ -52,19 +52,25 @@
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>10000</width>
|
||||
<height>100</height>
|
||||
<height>110</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy" >
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy" >
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="tabKeyNavigation" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize" >
|
||||
<size>
|
||||
<width>32</width>
|
||||
<height>32</height>
|
||||
<width>40</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="movement" >
|
||||
@ -77,14 +83,11 @@
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="spacing" >
|
||||
<number>20</number>
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="viewMode" >
|
||||
<enum>QListView::IconMode</enum>
|
||||
</property>
|
||||
<property name="uniformItemSizes" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -332,8 +335,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>847</width>
|
||||
<height>553</height>
|
||||
<width>857</width>
|
||||
<height>552</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QGridLayout" >
|
||||
@ -380,7 +383,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>865</width>
|
||||
<height>86</height>
|
||||
<height>79</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize" >
|
||||
@ -425,9 +428,9 @@
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>798</y>
|
||||
<y>795</y>
|
||||
<width>865</width>
|
||||
<height>24</height>
|
||||
<height>27</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="mouseTracking" >
|
||||
|
@ -1,4 +1,4 @@
|
||||
import os, sys, glob
|
||||
import os, sys, glob, shutil
|
||||
import sipconfig
|
||||
if os.environ.get('PYQT4PATH', None):
|
||||
print os.environ['PYQT4PATH']
|
||||
@ -37,7 +37,7 @@ makefile = pyqtconfig.QtGuiModuleMakefile (
|
||||
# ".dll" extension on Windows).
|
||||
if 'linux' in sys.platform:
|
||||
for f in glob.glob('../../.build/libpictureflow.a'):
|
||||
os.link(f, './'+os.path.basename(f))
|
||||
shutil.copyfile(f, os.path.basename(f))
|
||||
makefile.extra_lib_dirs = ['.']
|
||||
else:
|
||||
makefile.extra_lib_dirs = ['..\\..\\.build\\release', '../../.build', '.']
|
||||
|
@ -7,9 +7,9 @@ import re, os
|
||||
from PyQt4.QtGui import QListView, QIcon, QFont, QLabel, QListWidget, \
|
||||
QListWidgetItem, QTextCharFormat, QApplication, \
|
||||
QSyntaxHighlighter, QCursor, QColor, QWidget, \
|
||||
QAbstractItemDelegate, QPixmap
|
||||
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, QRect, SIGNAL, \
|
||||
QObject, QRegExp, QRectF
|
||||
QAbstractItemDelegate, QPixmap, QStyle, QFontMetrics
|
||||
from PyQt4.QtCore import QAbstractListModel, QVariant, Qt, SIGNAL, \
|
||||
QObject, QRegExp, QString
|
||||
|
||||
from calibre.gui2.jobs import DetailView
|
||||
from calibre.gui2 import human_readable, NONE, TableView, qstring_to_unicode, error_dialog
|
||||
@ -123,40 +123,43 @@ class LocationDelegate(QAbstractItemDelegate):
|
||||
|
||||
def __init__(self):
|
||||
QAbstractItemDelegate.__init__(self)
|
||||
self.icon_rect = QRect(0, 10, 150, 45)
|
||||
self.buffer = 5
|
||||
self.pixmap = QPixmap(40, 40)
|
||||
self.text = QString('Reader\n999.9 MB Available202')
|
||||
|
||||
def get_rects(self, index, option):
|
||||
row = index.row()
|
||||
irect = QRect(self.icon_rect)
|
||||
irect.translate(row*(irect.width()+self.buffer), 0)
|
||||
trect = irect.translated(0, irect.height())
|
||||
trect.adjust(0, 7, 0, 0)
|
||||
return irect.adjusted(50, 0, -50, 0), trect
|
||||
def rects(self, option):
|
||||
style = QApplication.style()
|
||||
font = QFont(option.font)
|
||||
font.setBold(True)
|
||||
irect = style.itemPixmapRect(option.rect, Qt.AlignHCenter|Qt.AlignTop, self.pixmap)
|
||||
trect = style.itemTextRect(QFontMetrics(font), option.rect,
|
||||
Qt.AlignHCenter|Qt.AlignTop, True, self.text)
|
||||
trect.moveTop(irect.bottom())
|
||||
return irect, trect
|
||||
|
||||
def sizeHint(self, option, index):
|
||||
irect, trect = self.get_rects(index, option)
|
||||
irect, trect = self.rects(option)
|
||||
return irect.united(trect).size()
|
||||
|
||||
def paint(self, painter, option, index):
|
||||
font = QFont()
|
||||
font.setPointSize(9)
|
||||
icon = QIcon(index.model().data(index, Qt.DecorationRole))
|
||||
highlight = getattr(index.model(), 'highlight_row', -1) == index.row()
|
||||
text = index.model().data(index, Qt.DisplayRole).toString()
|
||||
style = QApplication.style()
|
||||
painter.save()
|
||||
irect, trect = self.get_rects(index, option)
|
||||
|
||||
mode = QIcon.Normal
|
||||
if highlight:
|
||||
font.setBold(True)
|
||||
mode = QIcon.Active
|
||||
|
||||
if hasattr(QStyle, 'CE_ItemViewItem'):
|
||||
QApplication.style().drawControl(QStyle.CE_ItemViewItem, option, painter)
|
||||
highlight = getattr(index.model(), 'highlight_row', -1) == index.row()
|
||||
mode = QIcon.Active if highlight else QIcon.Normal
|
||||
pixmap = QIcon(index.model().data(index, Qt.DecorationRole)).pixmap(self.pixmap.size())
|
||||
pixmap = style.generatedIconPixmap(mode, pixmap, option)
|
||||
text = index.model().data(index, Qt.DisplayRole).toString()
|
||||
irect, trect = self.rects(option)
|
||||
style.drawItemPixmap(painter, irect, Qt.AlignHCenter|Qt.AlignTop, pixmap)
|
||||
font = QFont(option.font)
|
||||
font.setBold(highlight)
|
||||
painter.setFont(font)
|
||||
icon.paint(painter, irect, Qt.AlignHCenter|Qt.AlignTop, mode, QIcon.On)
|
||||
painter.drawText(QRectF(trect), Qt.AlignTop|Qt.AlignHCenter, text)
|
||||
style.drawItemText(painter, trect, Qt.AlignHCenter|Qt.AlignBottom,
|
||||
option.palette, True, text)
|
||||
painter.restore()
|
||||
|
||||
|
||||
|
||||
class LocationModel(QAbstractListModel):
|
||||
def __init__(self, parent):
|
||||
QAbstractListModel.__init__(self, parent)
|
||||
|
@ -15,6 +15,7 @@ from calibre.gui2 import SingleApplication
|
||||
from calibre.ebooks.metadata.meta import get_metadata
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
from calibre.library.database import text_to_tokens
|
||||
from calibre.ebooks.metadata.opf import OPFCreator, OPFReader
|
||||
|
||||
FIELDS = set(['title', 'authors', 'publisher', 'rating', 'timestamp', 'size', 'tags', 'comments', 'series', 'series_index', 'formats'])
|
||||
|
||||
@ -304,9 +305,67 @@ do nothing.
|
||||
do_remove_format(get_db(dbpath, opts), id, fmt)
|
||||
return 0
|
||||
|
||||
def do_show_metadata(db, id, as_opf):
|
||||
if not db.has_id(id):
|
||||
raise ValueError('Id #%d is not present in database.'%id)
|
||||
mi = db.get_metadata(id, index_is_id=True)
|
||||
if as_opf:
|
||||
mi = OPFCreator(os.getcwd(), mi)
|
||||
mi.render(sys.stdout)
|
||||
else:
|
||||
print mi
|
||||
|
||||
def command_show_metadata(args, dbpath):
|
||||
parser = get_parser(_(
|
||||
'''
|
||||
%prog show_metadata [options] id
|
||||
|
||||
Show the metadata stored in the calibre database for the book identified by id.
|
||||
id is an id number from the list command.
|
||||
'''))
|
||||
parser.add_option('--as-opf', default=False, action='store_true',
|
||||
help=_('Print metadata in OPF form (XML)'))
|
||||
opts, args = parser.parse_args(sys.argv[1:]+args)
|
||||
if len(args) < 2:
|
||||
parser.print_help()
|
||||
print
|
||||
print _('You must specify an id')
|
||||
return 1
|
||||
id = int(args[1])
|
||||
do_show_metadata(get_db(dbpath, opts), id, opts.as_opf)
|
||||
return 0
|
||||
|
||||
def do_set_metadata(db, id, stream):
|
||||
mi = OPFReader(stream)
|
||||
db.set_metadata(id, mi)
|
||||
do_show_metadata(db, id, False)
|
||||
if SingleApplication is not None:
|
||||
sa = SingleApplication('calibre GUI')
|
||||
sa.send_message('refreshdb:')
|
||||
|
||||
def command_set_metadata(args, dbpath):
|
||||
parser = get_parser(_(
|
||||
'''
|
||||
%prog set_metadata [options] id /path/to/metadata.opf
|
||||
|
||||
Set the metadata stored in the calibre database for the book identified by id
|
||||
from the OPF file metadata.opf. id is an id number from the list command. You
|
||||
can get a quick feel for the OPF format by using the --as-opf switch to the
|
||||
show_metadata command.
|
||||
'''))
|
||||
opts, args = parser.parse_args(sys.argv[1:]+args)
|
||||
if len(args) < 3:
|
||||
parser.print_help()
|
||||
print
|
||||
print _('You must specify an id and a metadata file')
|
||||
return 1
|
||||
id, opf = int(args[1]), open(args[2], 'rb')
|
||||
do_set_metadata(get_db(dbpath, opts), id, opf)
|
||||
return 0
|
||||
|
||||
def main(args=sys.argv):
|
||||
commands = ('list', 'add', 'remove', 'add_format', 'remove_format')
|
||||
commands = ('list', 'add', 'remove', 'add_format', 'remove_format',
|
||||
'show_metadata', 'set_metadata')
|
||||
parser = OptionParser(_(
|
||||
'''\
|
||||
%%prog command [options] [arguments]
|
||||
|
@ -911,13 +911,19 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
def title(self, index, index_is_id=False):
|
||||
if not index_is_id:
|
||||
return self.data[index][1]
|
||||
return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0]
|
||||
try:
|
||||
return self.conn.execute('SELECT title FROM meta WHERE id=?',(index,)).fetchone()[0]
|
||||
except:
|
||||
return _('Unknown')
|
||||
|
||||
def authors(self, index, index_is_id=False):
|
||||
''' Authors as a comma separated list or None'''
|
||||
if not index_is_id:
|
||||
return self.data[index][2]
|
||||
return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0]
|
||||
try:
|
||||
return self.conn.execute('SELECT authors FROM meta WHERE id=?',(index,)).fetchone()[0]
|
||||
except:
|
||||
pass
|
||||
|
||||
def isbn(self, idx, index_is_id=False):
|
||||
id = idx if index_is_id else self.id(idx)
|
||||
@ -929,22 +935,22 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
|
||||
def publisher(self, index, index_is_id=False):
|
||||
if index_is_id:
|
||||
return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (id,)).fetchone()[0]
|
||||
return self.conn.execute('SELECT publisher FROM meta WHERE id=?', (index,)).fetchone()[0]
|
||||
return self.data[index][3]
|
||||
|
||||
def rating(self, index, index_is_id=False):
|
||||
if index_is_id:
|
||||
return self.conn.execute('SELECT rating FROM meta WHERE id=?', (id,)).fetchone()[0]
|
||||
return self.conn.execute('SELECT rating FROM meta WHERE id=?', (index,)).fetchone()[0]
|
||||
return self.data[index][4]
|
||||
|
||||
def timestamp(self, index, index_is_id=False):
|
||||
if index_is_id:
|
||||
return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (id,)).fetchone()[0]
|
||||
return self.conn.execute('SELECT timestamp FROM meta WHERE id=?', (index,)).fetchone()[0]
|
||||
return self.data[index][5]
|
||||
|
||||
def max_size(self, index, index_is_id=False):
|
||||
if index_is_id:
|
||||
return self.conn.execute('SELECT size FROM meta WHERE id=?', (id,)).fetchone()[0]
|
||||
return self.conn.execute('SELECT size FROM meta WHERE id=?', (index,)).fetchone()[0]
|
||||
return self.data[index][6]
|
||||
|
||||
def cover(self, index, index_is_id=False):
|
||||
@ -1251,6 +1257,8 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
'''
|
||||
Set metadata for the book C{id} from the L{MetaInformation} object C{mi}
|
||||
'''
|
||||
if mi.title:
|
||||
self.set_title(id, mi.title)
|
||||
if not mi.authors:
|
||||
mi.authors = ['Unknown']
|
||||
authors = []
|
||||
@ -1516,6 +1524,9 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
|
||||
def has_book(self, mi):
|
||||
return bool(self.conn.execute('SELECT id FROM books where title=?', (mi.title,)).fetchone())
|
||||
|
||||
def has_id(self, id):
|
||||
return self.conn.execute('SELECT id FROM books where id=?', (id,)).fetchone() is not None
|
||||
|
||||
def recursive_import(self, root, single_book_per_directory=True):
|
||||
root = os.path.abspath(root)
|
||||
duplicates = []
|
||||
|
@ -131,7 +131,7 @@ Why does |app| show only some of my fonts on OS X?
|
||||
|
||||
The graphical user interface of |app| is not starting on Windows?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file libprs500.log (in the same places as library1.db) and post its contents in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion>`_.
|
||||
If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file calibre.log (in the same places as library1.db) and post its contents in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion>`_.
|
||||
|
||||
I want some feature added to |app|. What can I do?
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -17,7 +17,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#~ msgid ""
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -11,13 +11,13 @@ msgstr ""
|
||||
"Project-Id-Version: es\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2008-06-12 20:18+0000\n"
|
||||
"PO-Revision-Date: 2008-06-12 22:40+0000\n"
|
||||
"PO-Revision-Date: 2008-06-15 08:31+0000\n"
|
||||
"Last-Translator: S. Dorscht <Unknown>\n"
|
||||
"Language-Team: Spanish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#~ msgid ""
|
||||
@ -151,7 +151,7 @@ msgstr "Creado por "
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:146
|
||||
#: /home/kovid/work/calibre/src/calibre/devices/prs505/driver.py:174
|
||||
msgid "Unable to detect the %s disk drive. Try rebooting."
|
||||
msgstr ""
|
||||
msgstr "No se ha podido detectar la unidad de disco %s. Trate de reiniciar."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/__init__.py:73
|
||||
msgid "Set the title. Default: filename."
|
||||
@ -671,7 +671,6 @@ msgid "Creating XML..."
|
||||
msgstr "Creando XML..."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/ebooks/lrf/lrfparser.py:156
|
||||
#, fuzzy
|
||||
msgid "LRS written to "
|
||||
msgstr "LRS escrito en "
|
||||
|
||||
@ -1130,7 +1129,6 @@ msgid "Show &text in toolbar buttons"
|
||||
msgstr "Mostrar &texto en los botones de la barra de herramientas"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config_ui.py:219
|
||||
#, fuzzy
|
||||
msgid "Free unused diskspace from the database"
|
||||
msgstr "Espacio de disco disponible de la base de datos"
|
||||
|
||||
@ -1896,18 +1894,16 @@ msgstr ""
|
||||
"actual"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:63
|
||||
#, fuzzy
|
||||
msgid "No recipe selected"
|
||||
msgstr "No hay ninguna receta seleccionada"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:69
|
||||
#, fuzzy
|
||||
msgid "The attached file: %s is a recipe to download %s."
|
||||
msgstr "el archivo adjunto: %s es una receta para descargar %s"
|
||||
msgstr "El archivo adjunto: %s es una receta para descargar %s"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:70
|
||||
msgid "Recipe for "
|
||||
msgstr ""
|
||||
msgstr "Receta para "
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:86
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:96
|
||||
@ -1941,9 +1937,8 @@ msgid "Already exists"
|
||||
msgstr "Ya existe"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:121
|
||||
#, fuzzy
|
||||
msgid "This feed has already been added to the recipe"
|
||||
msgstr "el Feed ya se ha añadido a la receta"
|
||||
msgstr "Este Feed ya se ha añadido a la receta"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:162
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:171
|
||||
@ -1968,9 +1963,8 @@ msgid "A custom recipe named %s already exists. Do you want to replace it?"
|
||||
msgstr "una receta personalizada llamada %s ya existe. Quiere reemplazarla?"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:187
|
||||
#, fuzzy
|
||||
msgid "Choose a recipe file"
|
||||
msgstr "Seleccionarr un archivo de receta"
|
||||
msgstr "Seleccionar un archivo de receta"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/user_profiles.py:187
|
||||
msgid "Recipes"
|
||||
@ -2651,6 +2645,9 @@ msgid ""
|
||||
"href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">new features</a>. "
|
||||
"Visit the download page?"
|
||||
msgstr ""
|
||||
"%s se ha actualizado a la versión %s. Ver las <a "
|
||||
"href=\"http://calibre.kovidgoyal.net/wiki/Changelog\">nuevas "
|
||||
"características</a>. Visita la página de descarga?"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/gui2/main.py:1155
|
||||
msgid "Update available"
|
||||
@ -2833,6 +2830,8 @@ msgid ""
|
||||
"Path to the calibre database. Default is to use the path stored in the "
|
||||
"settings."
|
||||
msgstr ""
|
||||
"Camino a la base de datos calibre. El valor predeterminado es a usar la ruta "
|
||||
"almacenada en la configuración."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:80
|
||||
msgid ""
|
||||
@ -2840,6 +2839,9 @@ msgid ""
|
||||
"\n"
|
||||
"List the books available in the calibre database. \n"
|
||||
msgstr ""
|
||||
"%prog list [options]\n"
|
||||
"\n"
|
||||
"Mostrar los libros disponibles en la base de datos calibre. \n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:88
|
||||
msgid ""
|
||||
@ -2866,6 +2868,9 @@ msgid ""
|
||||
"please see the search related documentation in the User Manual. Default is "
|
||||
"to do no filtering."
|
||||
msgstr ""
|
||||
"Filtrar los resultados de la consulta de búsqueda. Para el formato de la "
|
||||
"consulta de búsqueda consulte la documentación relacionada con la búsqueda "
|
||||
"en el Manual del usuario. El valor predeterminado es a no hacer el filtrado."
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:101
|
||||
msgid "Invalid fields. Available fields:"
|
||||
@ -2891,12 +2896,20 @@ msgid ""
|
||||
"directories, see\n"
|
||||
"the directory related options below. \n"
|
||||
msgstr ""
|
||||
"%prog add [options] file1 file2 file3 ...\n"
|
||||
"\n"
|
||||
"Añadir los archivos especificados como libros a la base de datos. También "
|
||||
"puede especificar\n"
|
||||
"directorios, consulte las opciones relacionadas a los directorios más abajo. "
|
||||
"\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:204
|
||||
msgid ""
|
||||
"Assume that each directory has only a single logical book and that all files "
|
||||
"in it are different e-book formats of that book"
|
||||
msgstr ""
|
||||
"Supongamos que cada directorio tiene un solo libro lógico y que todos los "
|
||||
"archivos en este directorio son diferentes formatos de este libro"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:206
|
||||
msgid "Process directories recursively"
|
||||
@ -2922,6 +2935,12 @@ msgid ""
|
||||
"separated list of id numbers (you can get id numbers by using the list "
|
||||
"command). For example, 23,34,57-85\n"
|
||||
msgstr ""
|
||||
"%prog remove ids\n"
|
||||
"\n"
|
||||
"Eliminar los libros identificados por ID de la base de datos. ID debe ser "
|
||||
"una lista separada por comas de números de identificación (se puede obtener "
|
||||
"números de identificación utilizando el commando \"list\"). Por ejemplo, "
|
||||
"23,34,57-85\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:243
|
||||
msgid "You must specify at least one book to remove"
|
||||
@ -2953,6 +2972,13 @@ msgid ""
|
||||
"by using the list command. fmt should be a file extension like LRF or TXT or "
|
||||
"EPUB. If the logical book does not have fmt available, do nothing.\n"
|
||||
msgstr ""
|
||||
"\n"
|
||||
"%prog remove_format [options] id fmt\n"
|
||||
"\n"
|
||||
"Eliminar el formato fmt del libro lógico identificado por id. Usted puede "
|
||||
"obtener id utilizando el comando \"list\". fmt debe ser una extensión de "
|
||||
"archivo como LRF o TXT o EPUB. Si el libro lógico no tiene fmt disponible, "
|
||||
"no hacer nada.\n"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/library/cli.py:300
|
||||
msgid "You must specify an id and a format"
|
||||
@ -2976,11 +3002,11 @@ msgstr "Trabajo detenido por el usuario"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/fontconfig.py:124
|
||||
msgid "Could not initialize the fontconfig library"
|
||||
msgstr ""
|
||||
msgstr "No se ha podido inicializar la biblioteca fontconfig"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:53
|
||||
msgid "URL must have the scheme sftp"
|
||||
msgstr ""
|
||||
msgstr "La URL debe tener el régimen de sftp"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:57
|
||||
msgid "host must be of the form user@hostname"
|
||||
@ -2988,11 +3014,11 @@ msgstr ""
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:68
|
||||
msgid "Failed to negotiate SSH session: "
|
||||
msgstr ""
|
||||
msgstr "No se ha podido negociar período de sesiones SSH: "
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/utils/sftp.py:71
|
||||
msgid "Failed to authenticate with server: %s"
|
||||
msgstr ""
|
||||
msgstr "No se ha podido autenticar con el servidor: %s"
|
||||
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:56
|
||||
#: /home/kovid/work/calibre/src/calibre/web/feeds/__init__.py:77
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -15,7 +15,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -14,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
|
||||
#~ msgid ""
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -13,7 +13,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-14 07:16+0000\n"
|
||||
"X-Launchpad-Export-Date: 2008-06-15 22:20+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
@ -109,6 +109,8 @@ class Feed(object):
|
||||
if id in self.added_articles:
|
||||
return
|
||||
published = item.get('date_parsed', time.gmtime())
|
||||
if not published:
|
||||
published = time.gmtime()
|
||||
self.id_counter += 1
|
||||
self.added_articles.append(id)
|
||||
|
||||
|
234
upload.py
234
upload.py
@ -1,13 +1,19 @@
|
||||
#!/usr/bin/python
|
||||
import sys, os, shutil, time, tempfile, socket
|
||||
import sys, os, shutil, time, tempfile, socket, fcntl, struct
|
||||
sys.path.append('src')
|
||||
import subprocess
|
||||
from subprocess import check_call as _check_call
|
||||
from functools import partial
|
||||
#from pyvix.vix import Host, VIX_SERVICEPROVIDER_VMWARE_WORKSTATION
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(('google.com', 0))
|
||||
HOST=s.getsockname()[0]
|
||||
def get_ip_address(ifname):
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
return socket.inet_ntoa(fcntl.ioctl(
|
||||
s.fileno(),
|
||||
0x8915, # SIOCGIFADDR
|
||||
struct.pack('256s', ifname[:15])
|
||||
)[20:24])
|
||||
|
||||
HOST=get_ip_address('eth0')
|
||||
PROJECT=os.path.basename(os.getcwd())
|
||||
|
||||
from calibre import __version__, __appname__
|
||||
@ -21,11 +27,12 @@ TXT2LRF = "src/calibre/ebooks/lrf/txt/demo"
|
||||
BUILD_SCRIPT ='''\
|
||||
#!/bin/bash
|
||||
cd ~/build && \
|
||||
rsync -avz --exclude docs --exclude .bzr --exclude .build --exclude build --exclude dist --exclude "*.pyc" --exclude "*.pyo" rsync://%(host)s/work/%(project)s . && \
|
||||
rsync -avz --exclude src/calibre/plugins --exclude docs --exclude .bzr --exclude .build --exclude build --exclude dist --exclude "*.pyc" --exclude "*.pyo" rsync://%(host)s/work/%(project)s . && \
|
||||
cd %(project)s && \
|
||||
mkdir -p build dist && \
|
||||
mkdir -p build dist src/calibre/plugins && \
|
||||
%%s && \
|
||||
rm -rf build/* dist/* && \
|
||||
python %%s
|
||||
%%s %%s
|
||||
'''%dict(host=HOST, project=PROJECT)
|
||||
check_call = partial(_check_call, shell=True)
|
||||
#h = Host(hostType=VIX_SERVICEPROVIDER_VMWARE_WORKSTATION)
|
||||
@ -41,22 +48,24 @@ def installer_name(ext):
|
||||
return 'dist/%s-%s.%s'%(__appname__, __version__, ext)
|
||||
return 'dist/%s-%s-i686.%s'%(__appname__, __version__, ext)
|
||||
|
||||
def start_vm(vm, ssh_host, build_script, sleep):
|
||||
def start_vm(vm, ssh_host, build_script, sleep=75):
|
||||
vmware = ('vmware', '-q', '-x', '-n', vm)
|
||||
subprocess.Popen(vmware)
|
||||
t = tempfile.NamedTemporaryFile(suffix='.sh')
|
||||
t.write(build_script)
|
||||
t.flush()
|
||||
print 'Waiting for VM to startup'
|
||||
time.sleep(sleep)
|
||||
while subprocess.call('ping -q -c1 '+ssh_host, shell=True, stdout=open('/dev/null', 'w')) != 0:
|
||||
time.sleep(5)
|
||||
time.sleep(20)
|
||||
print 'Trying to SSH into VM'
|
||||
subprocess.check_call(('scp', t.name, ssh_host+':build-'+PROJECT))
|
||||
subprocess.check_call('ssh -t %s bash build-%s'%(ssh_host, PROJECT), shell=True)
|
||||
|
||||
def build_windows():
|
||||
installer = installer_name('exe')
|
||||
vm = '/vmware/Windows XP/Windows XP Professional.vmx'
|
||||
start_vm(vm, 'windows', BUILD_SCRIPT%'windows_installer.py', 75)
|
||||
subprocess.check_call(('ssh', 'windows', '/bin/bash', '~/build-'+PROJECT))
|
||||
start_vm(vm, 'windows', BUILD_SCRIPT%('python setup.py develop', 'python','windows_installer.py'))
|
||||
subprocess.check_call(('scp', 'windows:build/%s/dist/*.exe'%PROJECT, 'dist'))
|
||||
if not os.path.exists(installer):
|
||||
raise Exception('Failed to build installer '+installer)
|
||||
@ -66,160 +75,24 @@ def build_windows():
|
||||
def build_osx():
|
||||
installer = installer_name('dmg')
|
||||
vm = '/vmware/Mac OSX/Mac OSX.vmx'
|
||||
vmware = ('vmware', '-q', '-x', '-n', vm)
|
||||
start_vm(vm, 'osx', BUILD_SCRIPT%'osx_installer.py', 120)
|
||||
subprocess.check_call(('ssh', 'osx', '/bin/bash', '~/build-'+PROJECT))
|
||||
subprocess.check_call(('scp', 'windows:build/%s/dist/*.dmg'%PROJECT, 'dist'))
|
||||
python = '/Library/Frameworks/Python.framework/Versions/Current/bin/python'
|
||||
start_vm(vm, 'osx', BUILD_SCRIPT%('sudo %s setup.py develop'%python, python, 'osx_installer.py'))
|
||||
subprocess.check_call(('scp', 'osx:build/%s/dist/*.dmg'%PROJECT, 'dist'))
|
||||
if not os.path.exists(installer):
|
||||
raise Exception('Failed to build installer '+installer)
|
||||
subprocess.Popen(('ssh', 'osx', 'sudo', '/sbin/shutdown', '-h', 'now'))
|
||||
return os.path.basename(installer)
|
||||
|
||||
def _build_linux():
|
||||
cwd = os.getcwd()
|
||||
tbz2 = os.path.join(cwd, installer_name('tar.bz2'))
|
||||
SPEC="""\
|
||||
import os
|
||||
HOME = '%(home)s'
|
||||
PYINSTALLER = os.path.expanduser('~/build/pyinstaller')
|
||||
CALIBREPREFIX = HOME+'/work/%(project)s'
|
||||
CLIT = '/usr/bin/clit'
|
||||
PDFTOHTML = '/usr/bin/pdftohtml'
|
||||
LIBUNRAR = '/usr/lib/libunrar.so'
|
||||
QTDIR = '/usr/lib/qt4'
|
||||
QTDLLS = ('QtCore', 'QtGui', 'QtNetwork', 'QtSvg', 'QtXml')
|
||||
EXTRAS = ('/usr/lib/python2.5/site-packages/PIL', os.path.expanduser('~/ipython/IPython'))
|
||||
|
||||
import glob, sys, subprocess, tarfile
|
||||
CALIBRESRC = os.path.join(CALIBREPREFIX, 'src')
|
||||
CALIBREPLUGINS = os.path.join(CALIBRESRC, 'calibre', 'plugins')
|
||||
|
||||
subprocess.check_call(('/usr/bin/sudo', 'chown', '-R', 'kovid:users', glob.glob('/usr/lib/python*/site-packages/')[-1]))
|
||||
subprocess.check_call('rm -rf %%(py)s/dist/* %%(py)s/build/*'%%dict(py=PYINSTALLER), shell=True)
|
||||
|
||||
|
||||
loader = os.path.join('/tmp', 'calibre_installer_loader.py')
|
||||
if not os.path.exists(loader):
|
||||
open(loader, 'wb').write('''
|
||||
import sys, os
|
||||
sys.frozen_path = os.getcwd()
|
||||
os.chdir(os.environ.get("ORIGWD", "."))
|
||||
sys.path.insert(0, os.path.join(sys.frozen_path, "library.pyz"))
|
||||
sys.path.insert(0, sys.frozen_path)
|
||||
from PyQt4.QtCore import QCoreApplication
|
||||
QCoreApplication.setLibraryPaths([sys.frozen_path, os.path.join(sys.frozen_path, "plugins")])
|
||||
''')
|
||||
excludes = ['gtk._gtk', 'gtk.glade', 'qt', 'matplotlib.nxutils', 'matplotlib._cntr',
|
||||
'matplotlib.ttconv', 'matplotlib._image', 'matplotlib.ft2font',
|
||||
'matplotlib._transforms', 'matplotlib._agg', 'matplotlib.backends._backend_agg',
|
||||
'matplotlib.axes', 'matplotlib', 'matplotlib.pyparsing',
|
||||
'TKinter', 'atk', 'gobject._gobject', 'pango', 'PIL', 'Image', 'IPython']
|
||||
temp = ['keyword', 'codeop']
|
||||
|
||||
recipes = ['calibre', 'web', 'feeds', 'recipes']
|
||||
prefix = '.'.join(recipes)+'.'
|
||||
for f in glob.glob(os.path.join(CALIBRESRC, *(recipes+['*.py']))):
|
||||
temp.append(prefix + os.path.basename(f).partition('.')[0])
|
||||
hook = '/tmp/hook-calibre.py'
|
||||
open(hook, 'wb').write('hiddenimports = %%s'%%repr(temp) + '\\n')
|
||||
|
||||
sys.path.insert(0, CALIBRESRC)
|
||||
from calibre.linux import entry_points
|
||||
|
||||
executables, scripts = ['calibre_postinstall', 'parallel'], \
|
||||
[os.path.join(CALIBRESRC, 'calibre', 'linux.py'), os.path.join(CALIBRESRC, 'calibre', 'parallel.py')]
|
||||
|
||||
for entry in entry_points['console_scripts'] + entry_points['gui_scripts']:
|
||||
fields = entry.split('=')
|
||||
executables.append(fields[0].strip())
|
||||
scripts.append(os.path.join(CALIBRESRC, *map(lambda x: x.strip(), fields[1].split(':')[0].split('.')))+'.py')
|
||||
|
||||
recipes = Analysis(glob.glob(os.path.join(CALIBRESRC, 'calibre', 'web', 'feeds', 'recipes', '*.py')),
|
||||
pathex=[CALIBRESRC], hookspath=[os.path.dirname(hook)], excludes=excludes)
|
||||
analyses = [Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), loader, script],
|
||||
pathex=[PYINSTALLER, CALIBRESRC, CALIBREPLUGINS], excludes=excludes) for script in scripts]
|
||||
|
||||
pyz = TOC()
|
||||
binaries = TOC()
|
||||
|
||||
for a in analyses:
|
||||
pyz = a.pure + pyz
|
||||
binaries = a.binaries + binaries
|
||||
pyz = PYZ(pyz + recipes.pure, name='library.pyz')
|
||||
|
||||
built_executables = []
|
||||
for script, exe, a in zip(scripts, executables, analyses):
|
||||
built_executables.append(EXE(PYZ(TOC()),
|
||||
a.scripts+[('O','','OPTION'),],
|
||||
exclude_binaries=1,
|
||||
name=os.path.join('buildcalibre', exe),
|
||||
debug=False,
|
||||
strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
console=1))
|
||||
|
||||
print 'Adding plugins...'
|
||||
for f in glob.glob(os.path.join(CALIBREPLUGINS, '*.so')):
|
||||
binaries += [(os.path.basename(f), f, 'BINARY')]
|
||||
|
||||
print 'Adding external programs...'
|
||||
binaries += [('clit', CLIT, 'BINARY'), ('pdftohtml', PDFTOHTML, 'BINARY'),
|
||||
('libunrar.so', LIBUNRAR, 'BINARY')]
|
||||
qt = []
|
||||
for dll in QTDLLS:
|
||||
path = os.path.join(QTDIR, 'lib'+dll+'.so.4')
|
||||
qt.append((os.path.basename(path), path, 'BINARY'))
|
||||
binaries += qt
|
||||
|
||||
plugins = []
|
||||
plugdir = os.path.join(QTDIR, 'plugins')
|
||||
for dirpath, dirnames, filenames in os.walk(plugdir):
|
||||
for f in filenames:
|
||||
if not f.endswith('.so') or 'designer' in dirpath or 'codcs' in dirpath or 'sqldrivers' in dirpath : continue
|
||||
f = os.path.join(dirpath, f)
|
||||
plugins.append(('plugins/'+f.replace(plugdir, ''), f, 'BINARY'))
|
||||
binaries += plugins
|
||||
|
||||
manifest = '/tmp/manifest'
|
||||
open(manifest, 'wb').write('\\n'.join(executables))
|
||||
from calibre import __version__
|
||||
version = '/tmp/version'
|
||||
open(version, 'wb').write(__version__)
|
||||
coll = COLLECT(binaries, pyz, [('manifest', manifest, 'DATA'), ('version', version, 'DATA')],
|
||||
*built_executables,
|
||||
**dict(strip=True,
|
||||
upx=False,
|
||||
excludes=excludes,
|
||||
name='dist'))
|
||||
|
||||
os.chdir(os.path.join(HOMEPATH, 'calibre', 'dist'))
|
||||
for folder in EXTRAS:
|
||||
subprocess.check_call('cp -rf %%s .'%%folder, shell=True)
|
||||
|
||||
print 'Building tarball...'
|
||||
tf = tarfile.open('%(tarfile)s', 'w:bz2')
|
||||
|
||||
for f in os.listdir('.'):
|
||||
tf.add(f)
|
||||
|
||||
"""%dict(home='/mnt/hgfs/giskard/', tarfile=tbz2, project=PROJECT)
|
||||
os.chdir(os.path.expanduser('~/build/pyinstaller'))
|
||||
open('calibre/calibre.spec', 'wb').write(SPEC)
|
||||
try:
|
||||
subprocess.check_call(('/usr/bin/python', '-O', 'Build.py', 'calibre/calibre.spec'))
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
return os.path.basename(tbz2)
|
||||
|
||||
def build_linux():
|
||||
installer = installer_name('tar.bz2')
|
||||
vm = '/vmware/linux/libprs500-gentoo.vmx'
|
||||
vmware = ('vmware', '-q', '-x', '-n', vm)
|
||||
subprocess.Popen(vmware)
|
||||
print 'Waiting for linux to boot up...'
|
||||
time.sleep(75)
|
||||
check_call('ssh linux make -C /mnt/hgfs/giskard/work/%s all egg linux_binary'%PROJECT)
|
||||
check_call('ssh linux sudo poweroff')
|
||||
start_vm(vm, 'linux', BUILD_SCRIPT%('sudo python setup.py develop', 'python','linux_installer.py'))
|
||||
subprocess.check_call(('scp', 'linux:/tmp/%s'%os.path.basename(installer), 'dist'))
|
||||
if not os.path.exists(installer):
|
||||
raise Exception('Failed to build installer '+installer)
|
||||
subprocess.Popen(('ssh', 'linux', 'sudo', '/sbin/poweroff'))
|
||||
return os.path.basename(installer)
|
||||
|
||||
def build_installers():
|
||||
return build_linux(), build_windows(), build_osx()
|
||||
@ -267,18 +140,14 @@ def upload_user_manual():
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
def build_tarball():
|
||||
cwd = os.getcwd()
|
||||
def build_src_tarball():
|
||||
check_call('bzr export dist/calibre-%s.tar.bz2'%__version__)
|
||||
|
||||
def upload_tarball():
|
||||
def upload_src_tarball():
|
||||
check_call('ssh divok rm -f %s/calibre-\*.tar.bz2'%DOWNLOADS)
|
||||
check_call('scp dist/calibre-*.tar.bz2 divok:%s/'%DOWNLOADS)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
upload = len(sys.argv) < 2
|
||||
def stage_one():
|
||||
shutil.rmtree('build')
|
||||
os.mkdir('build')
|
||||
shutil.rmtree('docs')
|
||||
@ -288,17 +157,32 @@ def main():
|
||||
check_call('make', shell=True)
|
||||
tag_release()
|
||||
upload_demo()
|
||||
|
||||
def stage_two():
|
||||
subprocess.check_call('rm -rf dist/*', shell=True)
|
||||
build_installers()
|
||||
build_tarball()
|
||||
if upload:
|
||||
print 'Uploading installers...'
|
||||
upload_installers()
|
||||
print 'Uploading to PyPI'
|
||||
upload_tarball()
|
||||
upload_docs()
|
||||
upload_user_manual()
|
||||
check_call('python setup.py register bdist_egg --exclude-source-files upload')
|
||||
check_call('''rm -rf dist/* build/*''')
|
||||
build_src_tarball()
|
||||
|
||||
def stage_three():
|
||||
print 'Uploading installers...'
|
||||
upload_installers()
|
||||
print 'Uploading to PyPI'
|
||||
upload_src_tarball()
|
||||
upload_docs()
|
||||
upload_user_manual()
|
||||
check_call('python setup.py register bdist_egg --exclude-source-files upload')
|
||||
check_call('''rm -rf dist/* build/*''')
|
||||
|
||||
def main(args=sys.argv):
|
||||
print 'Starting stage one...'
|
||||
stage_one()
|
||||
print 'Starting stage two...'
|
||||
stage_two()
|
||||
print 'Starting stage three...'
|
||||
stage_three()
|
||||
print 'Finished'
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(main())
|
||||
|
@ -1,7 +1,7 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
''' Create a windows installer '''
|
||||
import sys, re, os, shutil, subprocess
|
||||
import sys, re, os, shutil, subprocess, zipfile
|
||||
from setup import VERSION, APPNAME, entry_points, scripts, basenames
|
||||
from distutils.core import setup
|
||||
from distutils.filelist import FileList
|
||||
@ -18,7 +18,7 @@ if os.path.exists(PY2EXE_DIR):
|
||||
class NSISInstaller(object):
|
||||
TEMPLATE = r'''
|
||||
; Do a Cyclic Redundancy Check to make sure the installer
|
||||
; was not corrupted by the download.
|
||||
; was not corrupted by the download.
|
||||
CRCCheck on
|
||||
|
||||
SetCompressor lzma
|
||||
@ -29,7 +29,7 @@ ShowUnInstDetails show
|
||||
;Include Modern UI
|
||||
!include "MUI2.nsh"
|
||||
!include "WinMessages.nsh"
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
;Variables
|
||||
Var STARTMENU_FOLDER
|
||||
@ -63,7 +63,7 @@ Loop:
|
||||
StrCmp "$R2" "$\r" RTrim
|
||||
StrCmp "$R2" ";" RTrim
|
||||
GoTo Done
|
||||
RTrim:
|
||||
RTrim:
|
||||
StrCpy $R1 "$R1" -1
|
||||
Goto Loop
|
||||
Done:
|
||||
@ -82,7 +82,7 @@ FunctionEnd
|
||||
; Call StrStr
|
||||
; Pop $R0
|
||||
; ($R0 at this point is "ass string")
|
||||
|
||||
|
||||
!macro StrStr un
|
||||
Function ${un}StrStr
|
||||
Exch $R1 ; st=haystack,old$R1, $R1=needle
|
||||
@ -123,7 +123,7 @@ Function AddToPath
|
||||
Push $3
|
||||
; don't add if the path doesn't exist
|
||||
IfFileExists "$0\*.*" "" AddToPath_done
|
||||
|
||||
|
||||
ReadEnvStr $1 PATH
|
||||
Push "$1;"
|
||||
Push "$0;"
|
||||
@ -146,7 +146,7 @@ Function AddToPath
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" "" AddToPath_done
|
||||
|
||||
|
||||
ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH"
|
||||
StrCmp $1 "" AddToPath_NTdoIt
|
||||
Push $1
|
||||
@ -156,7 +156,7 @@ Function AddToPath
|
||||
AddToPath_NTdoIt:
|
||||
WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $0
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
|
||||
AddToPath_done:
|
||||
Pop $3
|
||||
Pop $2
|
||||
@ -172,9 +172,9 @@ Function un.RemoveFromPath
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
|
||||
|
||||
IntFmt $6 "%%c" 26 # DOS EOF
|
||||
|
||||
|
||||
ReadRegStr $1 ${WriteEnvStr_RegKey} "PATH"
|
||||
StrCpy $5 $1 1 -1 # copy last char
|
||||
StrCmp $5 ";" +2 # if last char != ;
|
||||
@ -192,14 +192,14 @@ Function un.RemoveFromPath
|
||||
StrCpy $5 $1 -$4 # $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 # $6 is now the part after the path to remove
|
||||
StrCpy $3 $5$6
|
||||
|
||||
|
||||
StrCpy $5 $3 1 -1 # copy last char
|
||||
StrCmp $5 ";" 0 +2 # if last char == ;
|
||||
StrCpy $3 $3 -1 # remove last char
|
||||
|
||||
|
||||
WriteRegExpandStr ${WriteEnvStr_RegKey} "PATH" $3
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
|
||||
unRemoveFromPath_done:
|
||||
Pop $6
|
||||
Pop $5
|
||||
@ -219,13 +219,13 @@ FunctionEnd
|
||||
|
||||
;Default installation folder
|
||||
InstallDir "$PROGRAMFILES\${PRODUCT_NAME}"
|
||||
|
||||
|
||||
;Get installation folder from registry if available
|
||||
InstallDirRegKey HKCU "Software\${PRODUCT_NAME}" ""
|
||||
|
||||
|
||||
;Vista redirects $SMPROGRAMS to all users without this
|
||||
RequestExecutionLevel admin
|
||||
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
;Interface Settings
|
||||
|
||||
@ -241,25 +241,25 @@ FunctionEnd
|
||||
!insertmacro MUI_PAGE_COMPONENTS
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
;Start Menu Folder Page Configuration
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\${PRODUCT_NAME}"
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
|
||||
|
||||
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
|
||||
|
||||
; Finish page with option to run program
|
||||
; Disabled as GUI requires PATH and working directory to be set correctly
|
||||
;!define MUI_FINISHPAGE_RUN "$INSTDIR\${PRODUCT_NAME}.exe"
|
||||
;!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
;!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
!insertmacro MUI_UNPAGE_FINISH
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
;Languages
|
||||
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
;Installer Sections
|
||||
@ -268,7 +268,7 @@ Function .onInit
|
||||
; Prevent multiple instances of the installer from running
|
||||
System::Call 'kernel32::CreateMutexA(i 0, i 0, t "${PRODUCT_NAME}-setup") i .r1 ?e'
|
||||
Pop $R0
|
||||
|
||||
|
||||
StrCmp $R0 0 +3
|
||||
MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running."
|
||||
Abort
|
||||
@ -279,24 +279,24 @@ FunctionEnd
|
||||
Section "Main" "secmain"
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
|
||||
;ADD YOUR OWN FILES HERE...
|
||||
File /r "${PY2EXE_DIR}\*"
|
||||
File "${CLIT}"
|
||||
File "${PDFTOHTML}"
|
||||
File /r "${FONTCONFIG}\*"
|
||||
|
||||
|
||||
SetOutPath "$INSTDIR\ImageMagick"
|
||||
File /r "${IMAGEMAGICK}\*"
|
||||
|
||||
|
||||
|
||||
|
||||
SetOutPath "$SYSDIR"
|
||||
File "${LIBUNRAR_DIR}\unrar.dll"
|
||||
DetailPrint " "
|
||||
|
||||
|
||||
;Store installation folder
|
||||
WriteRegStr HKCU "Software\${PRODUCT_NAME}" "" $INSTDIR
|
||||
|
||||
|
||||
;Create uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" \
|
||||
@ -306,7 +306,7 @@ Section "Main" "secmain"
|
||||
|
||||
SetOutPath "$INSTDIR"
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
|
||||
|
||||
;Create shortcuts
|
||||
WriteIniStr "$INSTDIR\${PRODUCT_NAME}.url" "InternetShortcut" "URL" "${WEBSITE}"
|
||||
CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
|
||||
@ -317,11 +317,11 @@ Section "Main" "secmain"
|
||||
CreateShortCut "$DESKTOP\${PRODUCT_NAME}.lnk" "$INSTDIR\calibre.exe"
|
||||
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
|
||||
|
||||
;Add the installation directory to PATH for the commandline tools
|
||||
Push "$INSTDIR"
|
||||
Call AddToPath
|
||||
|
||||
|
||||
SectionEnd
|
||||
|
||||
Section /o "Device Drivers (only needed for PRS500)" "secdd"
|
||||
@ -338,13 +338,13 @@ Section /o "Device Drivers (only needed for PRS500)" "secdd"
|
||||
File "${LIBUSB_DIR}\libusb0.sys"
|
||||
;File "${LIBUSB_DIR}\libusb0_x64.dll"
|
||||
;File "${LIBUSB_DIR}\libusb0_x64.sys"
|
||||
|
||||
|
||||
; Uninstall USB drivers
|
||||
DetailPrint "Uninstalling any existing device drivers"
|
||||
ExecWait '"$INSTDIR\driver\devcon.exe" remove "USB\VID_054C&PID_029B"' $0
|
||||
DetailPrint "devcon returned exit code $0"
|
||||
|
||||
|
||||
|
||||
|
||||
DetailPrint "Installing USB driver for prs500..."
|
||||
ExecWait '"$INSTDIR\driver\devcon.exe" install "$INSTDIR\driver\prs500.inf" "USB\VID_054C&PID_029B"' $0
|
||||
DetailPrint "devcon returned exit code $0"
|
||||
@ -353,10 +353,10 @@ Section /o "Device Drivers (only needed for PRS500)" "secdd"
|
||||
Goto +2
|
||||
MessageBox MB_OK '1. If you have the SONY Connect Reader software installed: $\nGoto Add Remove Programs and uninstall the entry "Windows Driver Package - Sony Corporation (PRSUSB)". $\n$\n2. If your reader is connected to the computer, disconnect and reconnect it now.'
|
||||
DetailPrint " "
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
SectionEnd
|
||||
|
||||
;------------------------------------------------------------------------------------------------------
|
||||
@ -381,7 +381,7 @@ Section "un.DeviceDrivers"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
|
||||
|
||||
;ADD YOUR OWN FILES HERE...
|
||||
RMDir /r "$INSTDIR"
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
@ -404,15 +404,15 @@ Section "Uninstall"
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
|
||||
; Remove installation directory from PATH
|
||||
Push "$INSTDIR"
|
||||
Call un.RemoveFromPath
|
||||
Call un.RemoveFromPath
|
||||
SectionEnd
|
||||
'''
|
||||
def __init__(self, name, py2exe_dir, output_dir):
|
||||
self.installer = self.__class__.TEMPLATE % dict(name=name, py2exe_dir=py2exe_dir,
|
||||
version=VERSION,
|
||||
version=VERSION,
|
||||
outpath=os.path.abspath(output_dir))
|
||||
|
||||
def build(self):
|
||||
|
||||
def build(self):
|
||||
f = open('installer.nsi', 'w')
|
||||
path = f.name
|
||||
f.write(self.installer)
|
||||
@ -420,23 +420,23 @@ SectionEnd
|
||||
try:
|
||||
subprocess.check_call('"C:\Program Files\NSIS\makensis.exe" /V2 ' + path, shell=True)
|
||||
except:
|
||||
print path
|
||||
print path
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
|
||||
class BuildEXE(build_exe):
|
||||
manifest_resource_id = 0
|
||||
QT_PREFIX = r'C:\\Qt\\4.4.0'
|
||||
QT_PREFIX = r'C:\\Qt\\4.4.0'
|
||||
MANIFEST_TEMPLATE = '''
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity version="%(version)s"
|
||||
processorArchitecture="x86"
|
||||
name="net.kovidgoyal.%(prog)s"
|
||||
type="win32"
|
||||
/>
|
||||
<description>Ebook management application</description>
|
||||
/>
|
||||
<description>Ebook management application</description>
|
||||
<!-- Identify the application security requirements. -->
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
@ -474,7 +474,7 @@ class BuildEXE(build_exe):
|
||||
shutil.rmtree('.build', True)
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
|
||||
|
||||
def run(self):
|
||||
if not os.path.exists(self.dist_dir):
|
||||
os.makedirs(self.dist_dir)
|
||||
@ -493,7 +493,7 @@ class BuildEXE(build_exe):
|
||||
shutil.copyfile(qtsvgdll, os.path.join(self.dist_dir, os.path.basename(qtsvgdll)))
|
||||
qtxmldll = os.path.join(os.path.dirname(qtsvgdll), 'QtXml4.dll')
|
||||
print 'Adding', qtxmldll
|
||||
shutil.copyfile(qtxmldll,
|
||||
shutil.copyfile(qtxmldll,
|
||||
os.path.join(self.dist_dir, os.path.basename(qtxmldll)))
|
||||
print 'Adding plugins...',
|
||||
qt_prefix = self.QT_PREFIX
|
||||
@ -503,50 +503,45 @@ class BuildEXE(build_exe):
|
||||
for d in ('imageformats', 'codecs', 'iconengines'):
|
||||
print d,
|
||||
imfd = os.path.join(plugdir, d)
|
||||
tg = os.path.join(self.dist_dir, d)
|
||||
tg = os.path.join(self.dist_dir, d)
|
||||
if os.path.exists(tg):
|
||||
shutil.rmtree(tg)
|
||||
shutil.copytree(imfd, tg)
|
||||
|
||||
|
||||
|
||||
|
||||
print
|
||||
print 'Adding GUI main.py'
|
||||
f = zipfile.ZipFile(os.path.join('build', 'py2exe', 'library.zip'), 'a', zipfile.ZIP_DEFLATED)
|
||||
f.write('src\\calibre\\gui2\\main.py', 'calibre\\gui2\\main.py')
|
||||
f.close()
|
||||
|
||||
print
|
||||
print
|
||||
print 'Building Installer'
|
||||
installer = NSISInstaller(APPNAME, self.dist_dir, 'dist')
|
||||
installer.build()
|
||||
|
||||
|
||||
@classmethod
|
||||
def manifest(cls, prog):
|
||||
cls.manifest_resource_id += 1
|
||||
return (24, cls.manifest_resource_id,
|
||||
return (24, cls.manifest_resource_id,
|
||||
cls.MANIFEST_TEMPLATE % dict(prog=prog, version=VERSION+'.0'))
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
auto = '--auto' in sys.argv
|
||||
if auto:
|
||||
sys.argv.remove('--auto')
|
||||
sys.argv[1:2] = ['py2exe']
|
||||
if '--verbose' not in sys.argv:
|
||||
sys.argv.append('--quiet') #py2exe produces too much output by default
|
||||
subprocess.check_call('python setup.py develop', shell=True)
|
||||
if auto and not os.path.exists('dist\\auto'):
|
||||
print os.path.abspath('dist\\auto'), 'does not exist'
|
||||
return 1
|
||||
console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i])
|
||||
console = [dict(dest_base=basenames['console'][i], script=scripts['console'][i])
|
||||
for i in range(len(scripts['console']))]
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src'))
|
||||
setup(
|
||||
cmdclass = {'py2exe': BuildEXE},
|
||||
windows = [
|
||||
{'script' : scripts['gui'][0],
|
||||
{'script' : scripts['gui'][0],
|
||||
'dest_base' : APPNAME,
|
||||
'icon_resources' : [(1, 'icons/library.ico')],
|
||||
'other_resources' : [BuildEXE.manifest(APPNAME)],
|
||||
},
|
||||
{'script' : scripts['gui'][1],
|
||||
{'script' : scripts['gui'][1],
|
||||
'dest_base' : 'lrfviewer',
|
||||
'icon_resources' : [(1, 'icons/viewer.ico')],
|
||||
'other_resources' : [BuildEXE.manifest('lrfviewer')],
|
||||
@ -557,24 +552,22 @@ def main():
|
||||
'optimize' : 2,
|
||||
'dist_dir' : PY2EXE_DIR,
|
||||
'includes' : [
|
||||
'sip', 'pkg_resources', 'PyQt4.QtSvg',
|
||||
'mechanize', 'ClientForm', 'wmi',
|
||||
'win32file', 'pythoncom', 'rtf2xml',
|
||||
'sip', 'pkg_resources', 'PyQt4.QtSvg',
|
||||
'mechanize', 'ClientForm', 'wmi',
|
||||
'win32file', 'pythoncom', 'rtf2xml',
|
||||
'lxml', 'lxml._elementpath', 'genshi',
|
||||
'path', 'pydoc', 'IPython.Extensions.*',
|
||||
'calibre.web.feeds.recipes.*', 'pydoc',
|
||||
],
|
||||
],
|
||||
'packages' : ['PIL'],
|
||||
'excludes' : ["Tkconstants", "Tkinter", "tcl",
|
||||
"_imagingtk", "ImageTk", "FixTk",
|
||||
'excludes' : ["Tkconstants", "Tkinter", "tcl",
|
||||
"_imagingtk", "ImageTk", "FixTk",
|
||||
'pydoc'],
|
||||
'dll_excludes' : ['mswsock.dll'],
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
)
|
||||
if auto:
|
||||
subprocess.call(('shutdown', '-s', '-f', '-t', '01'))
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
x
Reference in New Issue
Block a user