mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -04:00
sync to pluginize
This commit is contained in:
commit
a2a1ea70f3
@ -3,7 +3,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
import sys, os, re, logging, time, subprocess, mimetypes, \
|
||||
__builtin__, warnings
|
||||
__builtin__, warnings, multiprocessing
|
||||
__builtin__.__dict__['dynamic_property'] = lambda(func): func(None)
|
||||
from htmlentitydefs import name2codepoint
|
||||
from math import floor
|
||||
@ -265,40 +265,7 @@ class StreamReadWrapper(object):
|
||||
|
||||
def detect_ncpus():
|
||||
"""Detects the number of effective CPUs in the system"""
|
||||
try:
|
||||
from PyQt4.QtCore import QThread
|
||||
ans = QThread.idealThreadCount()
|
||||
if ans > 0:
|
||||
return ans
|
||||
except:
|
||||
pass
|
||||
#for Linux, Unix and MacOS
|
||||
if hasattr(os, "sysconf"):
|
||||
if os.sysconf_names.has_key("SC_NPROCESSORS_ONLN"):
|
||||
#Linux and Unix
|
||||
ncpus = os.sysconf("SC_NPROCESSORS_ONLN")
|
||||
if isinstance(ncpus, int) and ncpus > 0:
|
||||
return ncpus
|
||||
else:
|
||||
#MacOS X
|
||||
try:
|
||||
return int(subprocess.Popen(('sysctl', '-n', 'hw.cpu'), stdout=subprocess.PIPE).stdout.read())
|
||||
except IOError: # Occassionally the system call gets interrupted
|
||||
try:
|
||||
return int(subprocess.Popen(('sysctl', '-n', 'hw.cpu'), stdout=subprocess.PIPE).stdout.read())
|
||||
except IOError:
|
||||
return 1
|
||||
except ValueError: # On some systems the sysctl call fails
|
||||
return 1
|
||||
|
||||
#for Windows
|
||||
if os.environ.has_key("NUMBER_OF_PROCESSORS"):
|
||||
ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]);
|
||||
if ncpus > 0:
|
||||
return ncpus
|
||||
#return the default value
|
||||
return 1
|
||||
|
||||
return multiprocessing.cpu_count()
|
||||
|
||||
def launch(path_or_url):
|
||||
if os.path.exists(path_or_url):
|
||||
|
@ -64,6 +64,10 @@ class OptionRecommendation(object):
|
||||
|
||||
self.validate_parameters()
|
||||
|
||||
@property
|
||||
def help(self):
|
||||
return self.option.help
|
||||
|
||||
def clone(self):
|
||||
return OptionRecommendation(recommended_value=self.recommended_value,
|
||||
level=self.level, option=self.option.clone())
|
||||
|
@ -3,7 +3,6 @@ __license__ = 'GPL 3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from itertools import izip
|
||||
|
||||
from calibre.customize import Plugin as _Plugin
|
||||
@ -141,16 +140,13 @@ class OutputProfile(Plugin):
|
||||
'if you want to produce a document intended to be read at a '
|
||||
'computer or on a range of devices.')
|
||||
|
||||
# ADE dies an agonizing, long drawn out death if HTML files have more
|
||||
# bytes than this.
|
||||
flow_size = -1
|
||||
# ADE runs screaming when it sees these characters
|
||||
remove_special_chars = re.compile(u'[\u200b\u00ad]')
|
||||
# ADE falls to the ground in a dead faint when it sees an <object>
|
||||
remove_object_tags = True
|
||||
# The image size for comics
|
||||
comic_screen_size = (584, 754)
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return ', '.join(tags)
|
||||
|
||||
class SonyReaderOutput(OutputProfile):
|
||||
|
||||
name = 'Sony Reader'
|
||||
@ -158,7 +154,6 @@ class SonyReaderOutput(OutputProfile):
|
||||
description = _('This profile is intended for the SONY PRS line. '
|
||||
'The 500/505/700 etc.')
|
||||
|
||||
flow_size = 270000
|
||||
screen_size = (600, 775)
|
||||
dpi = 168.451
|
||||
fbase = 12
|
||||
@ -236,6 +231,11 @@ class KindleOutput(OutputProfile):
|
||||
fbase = 16
|
||||
fsizes = [12, 12, 14, 16, 18, 20, 22, 24]
|
||||
|
||||
@classmethod
|
||||
def tags_to_string(cls, tags):
|
||||
return 'ttt '.join(tags)+'ttt '
|
||||
|
||||
|
||||
output_profiles = [OutputProfile, SonyReaderOutput, MSReaderOutput,
|
||||
MobipocketOutput, HanlinV3Output, CybookG3Output, KindleOutput,
|
||||
SonyReaderLandscapeOutput]
|
||||
|
@ -108,6 +108,11 @@ def debug_device_driver():
|
||||
drives.append((str(drive.PNPDeviceID), 'No mount points found'))
|
||||
for drive in drives:
|
||||
print '\t', drive
|
||||
if isosx:
|
||||
from calibre.devices.usbms.device import Device
|
||||
raw = Device.run_ioreg()
|
||||
open('/tmp/ioreg.txt', 'wb').write(raw)
|
||||
print 'ioreg output saved to /tmp/ioreg.txt'
|
||||
from calibre.devices import devices
|
||||
for dev in devices():
|
||||
print 'Looking for', dev.__name__
|
||||
|
@ -231,13 +231,19 @@ class Device(DeviceConfig, DevicePlugin):
|
||||
self._card_a_prefix = drives.get('carda', None)
|
||||
self._card_b_prefix = drives.get('cardb', None)
|
||||
|
||||
@classmethod
|
||||
def run_ioreg(cls, raw=None):
|
||||
if raw is not None:
|
||||
return raw
|
||||
ioreg = '/usr/sbin/ioreg'
|
||||
if not os.access(ioreg, os.X_OK):
|
||||
ioreg = 'ioreg'
|
||||
return subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
|
||||
|
||||
def get_osx_mountpoints(self, raw=None):
|
||||
if raw is None:
|
||||
ioreg = '/usr/sbin/ioreg'
|
||||
if not os.access(ioreg, os.X_OK):
|
||||
ioreg = 'ioreg'
|
||||
raw = subprocess.Popen((ioreg+' -w 0 -S -c IOMedia').split(),
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
raw = self.run_ioreg(raw)
|
||||
lines = raw.splitlines()
|
||||
names = {}
|
||||
|
||||
|
@ -19,6 +19,10 @@ def supported_input_formats():
|
||||
fmts.add(x)
|
||||
return fmts
|
||||
|
||||
INPUT_FORMAT_PREFERENCES = ['cbr', 'cbz', 'cbc', 'lit', 'mobi', 'prc', 'azw', 'fb2', 'html',
|
||||
'rtf', 'pdf', 'txt', 'pdb']
|
||||
OUTPUT_FORMAT_PREFERENCES = ['epub', 'mobi', 'lit', 'pdf', 'pdb', 'txt']
|
||||
|
||||
class OptionValues(object):
|
||||
pass
|
||||
|
||||
@ -114,7 +118,7 @@ OptionRecommendation(name='font_size_mapping',
|
||||
),
|
||||
|
||||
OptionRecommendation(name='line_height',
|
||||
recommended_value=None, level=OptionRecommendation.LOW,
|
||||
recommended_value=0, level=OptionRecommendation.LOW,
|
||||
help=_('The line height in pts. Controls spacing between consecutive '
|
||||
'lines of text. By default no line height manipulation is '
|
||||
'performed.'
|
||||
@ -463,6 +467,12 @@ OptionRecommendation(name='list_recipes',
|
||||
if rec.option == name:
|
||||
return rec
|
||||
|
||||
def get_option_help(self, name):
|
||||
rec = self.get_option_by_name(name)
|
||||
help = getattr(rec, 'help', None)
|
||||
if help is not None:
|
||||
return help.replace('%default', str(rec.recommended_value))
|
||||
|
||||
def merge_plugin_recommendations(self):
|
||||
for source in (self.input_plugin, self.output_plugin):
|
||||
for name, val, level in source.recommendations:
|
||||
@ -598,7 +608,7 @@ OptionRecommendation(name='list_recipes',
|
||||
|
||||
from calibre.ebooks.oeb.transforms.flatcss import CSSFlattener
|
||||
fbase = self.opts.base_font_size
|
||||
if fbase == 0:
|
||||
if fbase < 1e-4:
|
||||
fbase = float(self.opts.dest.fbase)
|
||||
fkey = self.opts.font_size_mapping
|
||||
if fkey is None:
|
||||
@ -618,8 +628,11 @@ OptionRecommendation(name='list_recipes',
|
||||
if self.output_plugin.file_type == 'lrf':
|
||||
self.opts.insert_blank_line = False
|
||||
self.opts.remove_paragraph_spacing = False
|
||||
line_height = self.opts.line_height
|
||||
if line_height < 1e-4:
|
||||
line_height = None
|
||||
flattener = CSSFlattener(fbase=fbase, fkey=fkey,
|
||||
lineh=self.opts.line_height,
|
||||
lineh=line_height,
|
||||
untable=self.output_plugin.file_type in ('mobi','lit'),
|
||||
unfloat=self.output_plugin.file_type in ('mobi', 'lit'))
|
||||
flattener(self.oeb, self.opts)
|
||||
|
@ -1,21 +0,0 @@
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'Convert a comic in CBR/CBZ format to epub'
|
||||
|
||||
import sys
|
||||
from functools import partial
|
||||
from calibre.ebooks.lrf.comic.convert_from import do_convert, option_parser, config, main as _main
|
||||
|
||||
convert = partial(do_convert, output_format='epub')
|
||||
main = partial(_main, output_format='epub')
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
if False:
|
||||
option_parser
|
||||
config
|
||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, shutil
|
||||
import os, shutil, re
|
||||
from urllib import unquote
|
||||
|
||||
from calibre.customize.conversion import OutputFormatPlugin
|
||||
@ -42,6 +42,13 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
)
|
||||
),
|
||||
|
||||
OptionRecommendation(name='flow_size', recommended_value=260,
|
||||
help=_('Split all HTML files larger than this size (in KB). '
|
||||
'This is necessary as most EPUB readers cannot handle large '
|
||||
'file sizes. The default of %defaultKB is the size required '
|
||||
'for Adobe Digital Editions.')
|
||||
),
|
||||
|
||||
|
||||
])
|
||||
|
||||
@ -104,7 +111,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
|
||||
from calibre.ebooks.oeb.transforms.split import Split
|
||||
split = Split(not self.opts.dont_split_on_page_breaks,
|
||||
max_flow_size=self.opts.output_profile.flow_size
|
||||
max_flow_size=self.opts.flow_size*1024
|
||||
)
|
||||
split(self.oeb, self.opts)
|
||||
|
||||
@ -243,14 +250,12 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
br.tail = ''
|
||||
br.tail += sibling.tail
|
||||
|
||||
|
||||
if self.opts.output_profile.remove_object_tags:
|
||||
for tag in XPath('//h:embed')(root):
|
||||
tag.getparent().remove(tag)
|
||||
for tag in XPath('//h:object')(root):
|
||||
if tag.get('type', '').lower().strip() in ('image/svg+xml',):
|
||||
continue
|
||||
tag.getparent().remove(tag)
|
||||
for tag in XPath('//h:embed')(root):
|
||||
tag.getparent().remove(tag)
|
||||
for tag in XPath('//h:object')(root):
|
||||
if tag.get('type', '').lower().strip() in ('image/svg+xml',):
|
||||
continue
|
||||
tag.getparent().remove(tag)
|
||||
|
||||
for tag in XPath('//h:title|//h:style')(root):
|
||||
if not tag.text:
|
||||
@ -276,5 +281,12 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
stylesheet.data.add('a[href] { color: blue; '
|
||||
'text-decoration: underline; cursor:pointer; }')
|
||||
|
||||
special_chars = re.compile(u'[\u200b\u00ad]')
|
||||
for elem in root.iterdescendants():
|
||||
if getattr(elem, 'text', False):
|
||||
elem.text = special_chars.sub('', elem.text)
|
||||
if getattr(elem, 'tail', False):
|
||||
elem.tail = special_chars.sub('', elem.tail)
|
||||
|
||||
|
||||
|
||||
|
@ -53,6 +53,7 @@ class LRFOptions(object):
|
||||
self.lrs = False
|
||||
self.minimize_memory_usage = False
|
||||
self.autorotation = opts.enable_autorotation
|
||||
self.header_separation = (self.profile.dpi/72.) * opts.header_separation
|
||||
|
||||
|
||||
for x in ('top', 'bottom', 'left', 'right'):
|
||||
@ -60,7 +61,7 @@ class LRFOptions(object):
|
||||
'margin_'+x))
|
||||
|
||||
for x in ('wordspace', 'header', 'header_format',
|
||||
'header_separation', 'minimum_indent', 'serif_family',
|
||||
'minimum_indent', 'serif_family',
|
||||
'render_tables_as_images', 'sans_family', 'mono_family',
|
||||
'text_size_multiplier_for_rendered_tables'):
|
||||
setattr(self, x, getattr(opts, x))
|
||||
@ -87,7 +88,7 @@ class LRFOutput(OutputFormatPlugin):
|
||||
'and %t by the title. Default is %default')
|
||||
),
|
||||
OptionRecommendation(name='header_separation', recommended_value=0,
|
||||
help=_('Add extra spacing below the header. Default is %default px.')
|
||||
help=_('Add extra spacing below the header. Default is %default pt.')
|
||||
),
|
||||
OptionRecommendation(name='minimum_indent', recommended_value=0,
|
||||
help=_('Minimum paragraph indent (the indent of the first line '
|
||||
@ -99,7 +100,7 @@ class LRFOutput(OutputFormatPlugin):
|
||||
'document has large or complex tables)')
|
||||
),
|
||||
OptionRecommendation(name='text_size_multiplier_for_rendered_tables',
|
||||
recommended_value=1,
|
||||
recommended_value=1.0,
|
||||
help=_('Multiply the size of text in rendered tables by this '
|
||||
'factor. Default is %default')
|
||||
),
|
||||
|
@ -1,70 +0,0 @@
|
||||
'''
|
||||
Convert any ebook format to Mobipocket.
|
||||
'''
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net ' \
|
||||
'and Marshall T. Vandegrift <llasram@gmail.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, os, glob, logging
|
||||
|
||||
from calibre.ebooks.epub.from_any import any2epub, formats, USAGE
|
||||
from calibre.ebooks.epub import config as common_config
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.mobi.writer import oeb2mobi, config as mobi_config
|
||||
|
||||
def config(defaults=None):
|
||||
c = common_config(defaults=defaults, name='mobi')
|
||||
c.remove_opt('profile')
|
||||
mobic = mobi_config(defaults=defaults)
|
||||
c.update(mobic)
|
||||
return c
|
||||
|
||||
def option_parser(usage=USAGE):
|
||||
usage = usage % ('Mobipocket', formats())
|
||||
parser = config().option_parser(usage=usage)
|
||||
return parser
|
||||
|
||||
def any2mobi(opts, path, notification=None):
|
||||
ext = os.path.splitext(path)[1]
|
||||
if not ext:
|
||||
raise ValueError('Unknown file type: '+path)
|
||||
ext = ext.lower()[1:]
|
||||
|
||||
if opts.output is None:
|
||||
opts.output = os.path.splitext(os.path.basename(path))[0]+'.mobi'
|
||||
|
||||
opts.output = os.path.abspath(opts.output)
|
||||
orig_output = opts.output
|
||||
|
||||
with TemporaryDirectory('_any2mobi') as tdir:
|
||||
oebdir = os.path.join(tdir, 'oeb')
|
||||
os.mkdir(oebdir)
|
||||
opts.output = os.path.join(tdir, 'dummy.epub')
|
||||
opts.profile = 'None'
|
||||
opts.dont_split_on_page_breaks = True
|
||||
orig_bfs = opts.base_font_size2
|
||||
opts.base_font_size2 = 0
|
||||
any2epub(opts, path, create_epub=False, oeb_cover=True, extract_to=oebdir)
|
||||
opts.base_font_size2 = orig_bfs
|
||||
opf = glob.glob(os.path.join(oebdir, '*.opf'))[0]
|
||||
opts.output = orig_output
|
||||
logging.getLogger('html2epub').info(_('Creating Mobipocket file from EPUB...'))
|
||||
oeb2mobi(opts, opf)
|
||||
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) < 2:
|
||||
parser.print_help()
|
||||
print 'No input file specified.'
|
||||
return 1
|
||||
any2mobi(opts, args[1])
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,44 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
'''
|
||||
import sys, os
|
||||
from calibre.ebooks.lrf.comic.convert_from import do_convert, option_parser, \
|
||||
ProgressBar, terminal_controller
|
||||
from calibre.ebooks.mobi.from_any import config, any2mobi
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
|
||||
|
||||
def convert(path_to_file, opts, notification=lambda m, p: p):
|
||||
pt = PersistentTemporaryFile('_comic2mobi.epub')
|
||||
pt.close()
|
||||
orig_output = opts.output
|
||||
opts.output = pt.name
|
||||
do_convert(path_to_file, opts, notification=notification, output_format='epub')
|
||||
opts = config('').parse()
|
||||
if orig_output is None:
|
||||
orig_output = os.path.splitext(path_to_file)[0]+'.mobi'
|
||||
opts.output = orig_output
|
||||
any2mobi(opts, pt.name)
|
||||
|
||||
def main(args=sys.argv):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) < 2:
|
||||
parser.print_help()
|
||||
print '\nYou must specify a file to convert'
|
||||
return 1
|
||||
|
||||
pb = ProgressBar(terminal_controller, _('Rendering comic pages...'),
|
||||
no_progress_bar=opts.no_progress_bar or getattr(opts, 'no_process', False))
|
||||
notification = pb.update
|
||||
|
||||
source = os.path.abspath(args[1])
|
||||
convert(source, opts, notification=notification)
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,74 +0,0 @@
|
||||
from __future__ import with_statement
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal kovid@kovidgoyal.net'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
'''
|
||||
Convert feeds to MOBI ebook
|
||||
'''
|
||||
|
||||
import sys, glob, os
|
||||
from calibre.web.feeds.main import config as feeds2disk_config, USAGE, run_recipe
|
||||
from calibre.ebooks.mobi.writer import config as oeb2mobi_config, oeb2mobi
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre import strftime, sanitize_file_name
|
||||
|
||||
def config(defaults=None):
|
||||
c = feeds2disk_config(defaults=defaults)
|
||||
c.remove('lrf')
|
||||
c.remove('epub')
|
||||
c.remove('mobi')
|
||||
c.remove('output_dir')
|
||||
c.update(oeb2mobi_config(defaults=defaults))
|
||||
c.remove('encoding')
|
||||
c.remove('source_profile')
|
||||
c.add_opt('output', ['-o', '--output'], default=None,
|
||||
help=_('Output file. Default is derived from input filename.'))
|
||||
return c
|
||||
|
||||
def option_parser():
|
||||
c = config()
|
||||
return c.option_parser(usage=USAGE)
|
||||
|
||||
def convert(opts, recipe_arg, notification=None):
|
||||
opts.lrf = False
|
||||
opts.epub = False
|
||||
opts.mobi = True
|
||||
if opts.debug:
|
||||
opts.verbose = 2
|
||||
parser = option_parser()
|
||||
with TemporaryDirectory('_feeds2mobi') as tdir:
|
||||
opts.output_dir = tdir
|
||||
recipe = run_recipe(opts, recipe_arg, parser, notification=notification)
|
||||
c = config()
|
||||
recipe_opts = c.parse_string(recipe.oeb2mobi_options)
|
||||
c.smart_update(recipe_opts, opts)
|
||||
opts = recipe_opts
|
||||
opf = glob.glob(os.path.join(tdir, '*.opf'))
|
||||
if not opf:
|
||||
raise Exception('Downloading of recipe: %s failed'%recipe_arg)
|
||||
opf = opf[0]
|
||||
|
||||
if opts.output is None:
|
||||
fname = recipe.title + strftime(recipe.timefmt) + '.mobi'
|
||||
opts.output = os.path.join(os.getcwd(), sanitize_file_name(fname))
|
||||
|
||||
print 'Generating MOBI...'
|
||||
opts.encoding = 'utf-8'
|
||||
opts.source_profile = 'Browser'
|
||||
oeb2mobi(opts, opf)
|
||||
|
||||
|
||||
def main(args=sys.argv, notification=None, handler=None):
|
||||
parser = option_parser()
|
||||
opts, args = parser.parse_args(args)
|
||||
if len(args) != 2 and opts.feeds is None:
|
||||
parser.print_help()
|
||||
return 1
|
||||
recipe_arg = args[1] if len(args) > 1 else None
|
||||
convert(opts, recipe_arg, notification=notification)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -70,9 +70,8 @@ class Jacket(object):
|
||||
tags = map(unicode, self.oeb.metadata.subject)
|
||||
except:
|
||||
tags = []
|
||||
tags = u'/'.join(tags)
|
||||
if tags:
|
||||
tags = '<b>Tags: </b>' + u'/%s/'%tags
|
||||
tags = '<b>Tags: </b>' + self.opts.dest.tags_to_string(tags)
|
||||
else:
|
||||
tags = ''
|
||||
try:
|
||||
|
@ -8,7 +8,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, re, sys, struct, zlib
|
||||
import os, re, struct, zlib
|
||||
|
||||
from calibre import CurrentDir
|
||||
from calibre.ebooks import DRMError
|
||||
|
@ -8,7 +8,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2009, John Schember <john@nachtimwald.com>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, re, struct, time
|
||||
import re, struct, time
|
||||
|
||||
class PdbHeaderReader(object):
|
||||
|
||||
@ -53,7 +53,8 @@ class PdbHeaderReader(object):
|
||||
|
||||
start = self.section_offset(number)
|
||||
if number == self.num_sections -1:
|
||||
end = os.stat(self.stream.name).st_size
|
||||
self.stream.seek(0, 2)
|
||||
end = self.stream.tell()
|
||||
else:
|
||||
end = self.section_offset(number + 1)
|
||||
self.stream.seek(start)
|
||||
|
@ -15,7 +15,6 @@ import os, glob
|
||||
|
||||
from calibre.customize.conversion import OutputFormatPlugin, \
|
||||
OptionRecommendation
|
||||
from calibre.ebooks.oeb.output import OEBOutput
|
||||
from calibre.ebooks.metadata.opf2 import OPF
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.pdf.writer import PDFWriter, ImagePDFWriter, PDFMetadata
|
||||
|
234
src/calibre/gui2/convert/__init__.py
Normal file
234
src/calibre/gui2/convert/__init__.py
Normal file
@ -0,0 +1,234 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os
|
||||
|
||||
from PyQt4.Qt import QWidget, QSpinBox, QDoubleSpinBox, QLineEdit, QTextEdit, \
|
||||
QCheckBox, QComboBox, Qt, QIcon, SIGNAL
|
||||
|
||||
from calibre.customize.conversion import OptionRecommendation
|
||||
from calibre.utils.config import config_dir
|
||||
from calibre.utils.lock import ExclusiveFile
|
||||
from calibre import sanitize_file_name
|
||||
|
||||
config_dir = os.path.join(config_dir, 'conversion')
|
||||
if not os.path.exists(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
|
||||
def name_to_path(name):
|
||||
return os.path.join(config_dir, sanitize_file_name(name)+'.py')
|
||||
|
||||
def save_defaults(name, recs):
|
||||
path = name_to_path(name)
|
||||
raw = str(recs)
|
||||
with open(path, 'wb'):
|
||||
pass
|
||||
with ExclusiveFile(path) as f:
|
||||
f.write(raw)
|
||||
save_defaults_ = save_defaults
|
||||
|
||||
def load_defaults(name):
|
||||
path = name_to_path(name)
|
||||
if not os.path.exists(path):
|
||||
open(path, 'wb').close()
|
||||
with ExclusiveFile(path) as f:
|
||||
raw = f.read()
|
||||
r = GuiRecommendations()
|
||||
if raw:
|
||||
r.from_string(raw)
|
||||
return r
|
||||
|
||||
def save_specifics(db, book_id, recs):
|
||||
raw = str(recs)
|
||||
db.set_conversion_options(book_id, 'PIPE', raw)
|
||||
|
||||
def load_specifics(db, book_id):
|
||||
raw = db.conversion_options(book_id, 'PIPE')
|
||||
r = GuiRecommendations()
|
||||
if raw:
|
||||
r.from_string(raw)
|
||||
return r
|
||||
|
||||
class GuiRecommendations(dict):
|
||||
|
||||
def __new__(cls, *args):
|
||||
dict.__new__(cls)
|
||||
obj = super(GuiRecommendations, cls).__new__(cls, *args)
|
||||
obj.disabled_options = set([])
|
||||
return obj
|
||||
|
||||
def to_recommendations(self, level=OptionRecommendation.LOW):
|
||||
ans = []
|
||||
for key, val in self.items():
|
||||
ans.append((key, val, level))
|
||||
return ans
|
||||
|
||||
def __str__(self):
|
||||
ans = ['{']
|
||||
for key, val in self.items():
|
||||
ans.append('\t'+repr(key)+' : '+repr(val)+',')
|
||||
ans.append('}')
|
||||
return '\n'.join(ans)
|
||||
|
||||
def from_string(self, raw):
|
||||
try:
|
||||
d = eval(raw)
|
||||
except SyntaxError:
|
||||
d = None
|
||||
if d:
|
||||
self.update(d)
|
||||
|
||||
def merge_recommendations(self, get_option, level, options):
|
||||
for name in options:
|
||||
opt = get_option(name)
|
||||
if opt is None: continue
|
||||
if opt.level == OptionRecommendation.HIGH:
|
||||
self[name] = opt.recommended_value
|
||||
self.disabled_options.add(name)
|
||||
elif opt.level > level or name not in self:
|
||||
self[name] = opt.recommended_value
|
||||
|
||||
|
||||
class Widget(QWidget):
|
||||
|
||||
TITLE = _('Unknown')
|
||||
ICON = ':/images/config.svg'
|
||||
HELP = ''
|
||||
|
||||
def __init__(self, parent, name, options):
|
||||
QWidget.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self._options = options
|
||||
self._name = name
|
||||
self._icon = QIcon(self.ICON)
|
||||
for name in self._options:
|
||||
if not hasattr(self, 'opt_'+name):
|
||||
raise Exception('Option %s missing in %s'%(name,
|
||||
self.__class__.__name__))
|
||||
|
||||
def initialize_options(self, get_option, get_help, db=None, book_id=None):
|
||||
'''
|
||||
:param get_option: A callable that takes one argument: the option name
|
||||
and returns the correspoing OptionRecommendation.
|
||||
:param get_help: A callable that takes the option name and return a help
|
||||
string.
|
||||
'''
|
||||
defaults = load_defaults(self._name)
|
||||
defaults.merge_recommendations(get_option, OptionRecommendation.LOW,
|
||||
self._options)
|
||||
|
||||
if db is not None:
|
||||
specifics = load_specifics(db, book_id)
|
||||
specifics.merge_recommendations(get_option, OptionRecommendation.HIGH,
|
||||
self._options)
|
||||
defaults.update(specifics)
|
||||
self.apply_recommendations(defaults)
|
||||
self.setup_help(get_help)
|
||||
|
||||
def commit_options(self, save_defaults=False):
|
||||
recs = self.create_recommendations()
|
||||
if save_defaults:
|
||||
save_defaults_(self._name, recs)
|
||||
return recs
|
||||
|
||||
def create_recommendations(self):
|
||||
recs = GuiRecommendations()
|
||||
for name in self._options:
|
||||
gui_opt = getattr(self, 'opt_'+name, None)
|
||||
if gui_opt is None: continue
|
||||
recs[name] = self.get_value(gui_opt)
|
||||
return recs
|
||||
|
||||
def apply_recommendations(self, recs):
|
||||
for name, val in recs.items():
|
||||
gui_opt = getattr(self, 'opt_'+name, None)
|
||||
if gui_opt is None: continue
|
||||
self.set_value(gui_opt, val)
|
||||
if name in getattr(recs, 'disabled_options', []):
|
||||
gui_opt.setDisabled(True)
|
||||
|
||||
|
||||
def get_value(self, g):
|
||||
ret = self.get_value_handler(g)
|
||||
if ret != 'this is a dummy return value, xcswx1avcx4x':
|
||||
return ret
|
||||
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
|
||||
return g.value()
|
||||
elif isinstance(g, (QLineEdit, QTextEdit)):
|
||||
func = getattr(g, 'toPlainText', getattr(g, 'text', None))()
|
||||
ans = unicode(func).strip()
|
||||
if not ans:
|
||||
ans = None
|
||||
return ans
|
||||
elif isinstance(g, QComboBox):
|
||||
return unicode(g.currentText())
|
||||
elif isinstance(g, QCheckBox):
|
||||
return bool(g.isChecked())
|
||||
else:
|
||||
raise Exception('Can\'t get value from %s'%type(g))
|
||||
|
||||
|
||||
def set_value(self, g, val):
|
||||
if self.set_value_handler(g, val):
|
||||
return
|
||||
if isinstance(g, (QSpinBox, QDoubleSpinBox)):
|
||||
g.setValue(val)
|
||||
elif isinstance(g, (QLineEdit, QTextEdit)):
|
||||
if not val: val = ''
|
||||
getattr(g, 'setPlainText', g.setText)(val)
|
||||
getattr(g, 'setCursorPosition', lambda x: x)(0)
|
||||
elif isinstance(g, QComboBox) and val:
|
||||
idx = g.findText(val, Qt.MatchFixedString)
|
||||
if idx < 0:
|
||||
g.addItem(val)
|
||||
idx = g.findText(val, Qt.MatchFixedString)
|
||||
g.setCurrentIndex(idx)
|
||||
elif isinstance(g, QCheckBox):
|
||||
g.setCheckState(Qt.Checked if bool(val) else Qt.Unchecked)
|
||||
else:
|
||||
raise Exception('Can\'t set value %s in %s'%(repr(val), type(g)))
|
||||
self.post_set_value(g, val)
|
||||
|
||||
def set_help(self, msg):
|
||||
if msg and getattr(msg, 'strip', lambda:True)():
|
||||
self.emit(SIGNAL('set_help(PyQt_PyObject)'), msg)
|
||||
|
||||
def setup_help(self, help_provider):
|
||||
for name in self._options:
|
||||
g = getattr(self, 'opt_'+name, None)
|
||||
if g is None:
|
||||
continue
|
||||
help = help_provider(name)
|
||||
if not help: continue
|
||||
g._help = help
|
||||
g.setToolTip(help.replace('<', '<').replace('>', '>'))
|
||||
g.setWhatsThis(help.replace('<', '<').replace('>', '>'))
|
||||
g.__class__.enterEvent = lambda obj, event: self.set_help(getattr(obj, '_help', obj.toolTip()))
|
||||
|
||||
|
||||
def set_value_handler(self, g, val):
|
||||
return False
|
||||
|
||||
def post_set_value(self, g, val):
|
||||
pass
|
||||
|
||||
def get_value_handler(self, g):
|
||||
return 'this is a dummy return value, xcswx1avcx4x'
|
||||
|
||||
def post_get_value(self, g):
|
||||
pass
|
||||
|
||||
def commit(self, save_defaults=False):
|
||||
return self.commit_options(save_defaults)
|
||||
|
||||
def config_title(self):
|
||||
return self.TITLE
|
||||
|
||||
def config_icon(self):
|
||||
return self._icon
|
||||
|
28
src/calibre/gui2/convert/comic_input.py
Normal file
28
src/calibre/gui2/convert/comic_input.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
from calibre.gui2.convert.comic_input_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
|
||||
class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Comic Input')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'comic_input',
|
||||
['colors', 'dont_normalize', 'keep_aspect_ratio', 'right2left',
|
||||
'despeckle', 'no_sort', 'no_process', 'landscape',
|
||||
'dont_sharpen', 'disable_trim', 'wide']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
self.opt_no_process.toggle()
|
||||
self.opt_no_process.toggle()
|
||||
|
||||
|
304
src/calibre/gui2/convert/comic_input.ui
Normal file
304
src/calibre/gui2/convert/comic_input.ui
Normal file
@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>599</width>
|
||||
<height>305</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Number of Colors:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_colors</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="opt_colors">
|
||||
<property name="minimum">
|
||||
<number>8</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>3200000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>8</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="opt_dont_normalize">
|
||||
<property name="text">
|
||||
<string>Disable &normalize</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QCheckBox" name="opt_keep_aspect_ratio">
|
||||
<property name="text">
|
||||
<string>Keep &aspect ratio</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QCheckBox" name="opt_dont_sharpen">
|
||||
<property name="text">
|
||||
<string>Disable &Sharpening</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="opt_disable_trim">
|
||||
<property name="text">
|
||||
<string>Disable &Trimming</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QCheckBox" name="opt_wide">
|
||||
<property name="text">
|
||||
<string>&Wide</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="opt_landscape">
|
||||
<property name="text">
|
||||
<string>&Landscape</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QCheckBox" name="opt_right2left">
|
||||
<property name="text">
|
||||
<string>&Right to left</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<widget class="QCheckBox" name="opt_no_sort">
|
||||
<property name="text">
|
||||
<string>Don't so&rt</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QCheckBox" name="opt_despeckle">
|
||||
<property name="text">
|
||||
<string>De&speckle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QCheckBox" name="opt_no_process">
|
||||
<property name="text">
|
||||
<string>&Disable comic processing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>label_3</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>184</x>
|
||||
<y>11</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>210</x>
|
||||
<y>44</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_colors</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>284</x>
|
||||
<y>17</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>371</x>
|
||||
<y>39</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_dont_normalize</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>107</x>
|
||||
<y>15</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>20</x>
|
||||
<y>67</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_keep_aspect_ratio</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>171</x>
|
||||
<y>11</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>38</x>
|
||||
<y>98</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_dont_sharpen</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>126</x>
|
||||
<y>12</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>110</x>
|
||||
<y>124</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_disable_trim</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>153</x>
|
||||
<y>5</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>67</x>
|
||||
<y>155</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_wide</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>164</x>
|
||||
<y>13</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>84</x>
|
||||
<y>180</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_landscape</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>129</x>
|
||||
<y>11</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>145</x>
|
||||
<y>209</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_right2left</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>183</x>
|
||||
<y>15</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>52</x>
|
||||
<y>225</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_no_sort</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>162</x>
|
||||
<y>11</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>105</x>
|
||||
<y>256</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_no_process</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_despeckle</receiver>
|
||||
<slot>setDisabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>254</x>
|
||||
<y>19</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>180</x>
|
||||
<y>296</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
23
src/calibre/gui2/convert/epub_output.py
Normal file
23
src/calibre/gui2/convert/epub_output.py
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
from calibre.gui2.convert.epub_output_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
|
||||
class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('EPUB Output')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'epub_output',
|
||||
['dont_split_on_page_breaks', 'flow_size']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
67
src/calibre/gui2/convert/epub_output.ui
Normal file
67
src/calibre/gui2/convert/epub_output.ui
Normal file
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_dont_split_on_page_breaks">
|
||||
<property name="text">
|
||||
<string>Do not &split on page breaks</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Split files &larger than:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_flow_size</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QSpinBox" name="opt_flow_size">
|
||||
<property name="suffix">
|
||||
<string> KB</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>1000000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>20</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>262</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
28
src/calibre/gui2/convert/look_and_feel.py
Normal file
28
src/calibre/gui2/convert/look_and_feel.py
Normal file
@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
from calibre.gui2.convert.look_and_feel_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
|
||||
class LookAndFeelWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Look & Feel')
|
||||
ICON = ':/images/lookfeel.svg'
|
||||
HELP = _('Control the look and feel of the output')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'look_and_feel',
|
||||
['dont_justify', 'extra_css', 'base_font_size',
|
||||
'font_size_mapping', 'insert_metadata', 'line_height',
|
||||
'linearize_tables', 'remove_first_image',
|
||||
'remove_paragraph_spacing', 'input_encoding']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
153
src/calibre/gui2/convert/look_and_feel.ui
Normal file
153
src/calibre/gui2/convert/look_and_feel.ui
Normal file
@ -0,0 +1,153 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout_6">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_18">
|
||||
<property name="text">
|
||||
<string>Base &font size:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_base_font_size</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QDoubleSpinBox" name="opt_base_font_size">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>0.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>30.000000000000000</double>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>15.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Line &height:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_line_height</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QDoubleSpinBox" name="opt_line_height">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_remove_paragraph_spacing">
|
||||
<property name="text">
|
||||
<string>Remove &spacing between paragraphs</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_dont_justify">
|
||||
<property name="text">
|
||||
<string>No text &justification</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_linearize_tables">
|
||||
<property name="text">
|
||||
<string>&Linearize tables</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_remove_first_image">
|
||||
<property name="text">
|
||||
<string>Remove &first image from source file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Font size &key:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_font_size_mapping</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="opt_font_size_mapping"/>
|
||||
</item>
|
||||
<item row="8" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_insert_metadata">
|
||||
<property name="text">
|
||||
<string>Insert &metadata at start of book</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Input character &encoding</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_input_encoding</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="2">
|
||||
<widget class="QLineEdit" name="opt_input_encoding"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Extra &CSS</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="0" column="0">
|
||||
<widget class="QTextEdit" name="opt_extra_css"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../work/calibre-pluginize/src/calibre/gui2/images.qrc"/>
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
48
src/calibre/gui2/convert/lrf_output.py
Normal file
48
src/calibre/gui2/convert/lrf_output.py
Normal file
@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
from calibre.gui2.convert.lrf_output_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
from calibre.gui2.widgets import FontFamilyModel
|
||||
|
||||
font_family_model = None
|
||||
|
||||
class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('LRF Output')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'lrf_output',
|
||||
['wordspace', 'header', 'header_format',
|
||||
'minimum_indent', 'serif_family',
|
||||
'render_tables_as_images', 'sans_family', 'mono_family',
|
||||
'text_size_multiplier_for_rendered_tables', 'autorotation',
|
||||
'header_separation', 'minimum_indent']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
global font_family_model
|
||||
if font_family_model is None:
|
||||
font_family_model = FontFamilyModel()
|
||||
self.font_family_model = font_family_model
|
||||
self.opt_serif_family.setModel(self.font_family_model)
|
||||
self.opt_sans_family.setModel(self.font_family_model)
|
||||
self.opt_mono_family.setModel(self.font_family_model)
|
||||
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
self.opt_header.toggle(), self.opt_header.toggle()
|
||||
self.opt_render_tables_as_images.toggle()
|
||||
self.opt_render_tables_as_images.toggle()
|
||||
|
||||
|
||||
def set_value_handler(self, g, val):
|
||||
if val is None and unicode(g.objectName()) in ('opt_serif_family',
|
||||
'opt_sans_family', 'opt_mono_family'):
|
||||
g.setCurrentIndex(0)
|
||||
return True
|
||||
return False
|
304
src/calibre/gui2/convert/lrf_output.ui
Normal file
304
src/calibre/gui2/convert/lrf_output.ui
Normal file
@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>481</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="opt_autorotation">
|
||||
<property name="text">
|
||||
<string>Enable &autorotation of wide images</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Wordspace:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_wordspace</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="opt_wordspace">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<double>1.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>20.000000000000000</double>
|
||||
</property>
|
||||
<property name="value">
|
||||
<double>2.500000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Minimum para. &indent:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_minimum_indent</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="opt_minimum_indent">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="opt_render_tables_as_images">
|
||||
<property name="text">
|
||||
<string>Render &tables as images</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Text size multiplier for text in rendered tables:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDoubleSpinBox" name="opt_text_size_multiplier_for_rendered_tables"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_header">
|
||||
<property name="text">
|
||||
<string>Add &header</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Header &separation:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_header_separation</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QDoubleSpinBox" name="opt_header_separation">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>Header &format:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_header_format</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="opt_header_format"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>&Embed fonts</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Serif font family:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_serif_family</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>S&ans-serif font family:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_sans_family</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>&Monospaced font family:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_mono_family</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="opt_serif_family"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opt_sans_family"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="opt_mono_family"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>opt_render_tables_as_images</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_text_size_multiplier_for_rendered_tables</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>76</x>
|
||||
<y>80</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>418</x>
|
||||
<y>105</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_header</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_header_separation</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>346</x>
|
||||
<y>144</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>408</x>
|
||||
<y>167</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_header</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>opt_header_format</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>124</x>
|
||||
<y>138</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>230</x>
|
||||
<y>208</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_render_tables_as_images</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>label_3</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>96</x>
|
||||
<y>76</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>31</x>
|
||||
<y>113</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_header</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>label_4</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>67</x>
|
||||
<y>144</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>72</x>
|
||||
<y>165</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>opt_header</sender>
|
||||
<signal>toggled(bool)</signal>
|
||||
<receiver>label_5</receiver>
|
||||
<slot>setEnabled(bool)</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>107</x>
|
||||
<y>141</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>102</x>
|
||||
<y>211</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
161
src/calibre/gui2/convert/metadata.py
Normal file
161
src/calibre/gui2/convert/metadata.py
Normal file
@ -0,0 +1,161 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import os, uuid
|
||||
|
||||
from PyQt4.Qt import QPixmap, SIGNAL
|
||||
|
||||
from calibre.gui2 import choose_images, error_dialog, pixmap_to_data
|
||||
from calibre.gui2.convert.metadata_ui import Ui_Form
|
||||
from calibre.ebooks.metadata import authors_to_string, string_to_authors, \
|
||||
MetaInformation
|
||||
from calibre.ebooks.metadata.opf2 import OPFCreator
|
||||
from calibre.ptempfile import PersistentTemporaryFile
|
||||
from calibre.gui2.convert import Widget
|
||||
|
||||
class MetadataWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Metadata')
|
||||
ICON = ':/images/dialog_information.svg'
|
||||
HELP = _('Set the metadata. The output file will contain as much of this '
|
||||
'metadata as possible.')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'metadata', ['prefer_metadata_cover'])
|
||||
self.db, self.book_id = db, book_id
|
||||
self.cover_changed = False
|
||||
if self.db is not None:
|
||||
self.initialize_metadata_options()
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
self.connect(self.cover_button, SIGNAL("clicked()"), self.select_cover)
|
||||
|
||||
def initialize_metadata_options(self):
|
||||
all_series = self.db.all_series()
|
||||
all_series.sort(cmp=lambda x, y : cmp(x[1], y[1]))
|
||||
for series in all_series:
|
||||
self.series.addItem(series[1])
|
||||
self.series.setCurrentIndex(-1)
|
||||
|
||||
mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
||||
self.title.setText(mi.title)
|
||||
if mi.authors:
|
||||
self.author.setText(authors_to_string(mi.authors))
|
||||
else:
|
||||
self.author.setText('')
|
||||
self.publisher.setText(mi.publisher if mi.publisher else '')
|
||||
self.author_sort.setText(mi.author_sort if mi.author_sort else '')
|
||||
self.tags.setText(', '.join(mi.tags if mi.tags else []))
|
||||
self.comment.setText(mi.comments if mi.comments else '')
|
||||
if mi.series:
|
||||
self.series.setCurrentIndex(self.series.findText(mi.series))
|
||||
if mi.series_index is not None:
|
||||
self.series_index.setValue(mi.series_index)
|
||||
|
||||
cover = self.db.cover(self.book_id, index_is_id=True)
|
||||
if cover:
|
||||
pm = QPixmap()
|
||||
pm.loadFromData(cover)
|
||||
if not pm.isNull():
|
||||
self.cover.setPixmap(pm)
|
||||
|
||||
def get_title_and_authors(self):
|
||||
title = unicode(self.title.text()).strip()
|
||||
if not title:
|
||||
title = _('Unknown')
|
||||
authors = unicode(self.author.text()).strip()
|
||||
authors = string_to_authors(authors) if authors else [_('Unknown')]
|
||||
return title, authors
|
||||
|
||||
def get_metadata(self):
|
||||
title, authors = self.get_title_and_authors()
|
||||
mi = MetaInformation(title, authors)
|
||||
publisher = unicode(self.publisher.text()).strip()
|
||||
if publisher:
|
||||
mi.publisher = publisher
|
||||
author_sort = unicode(self.author_sort.text()).strip()
|
||||
if author_sort:
|
||||
mi.author_sort = author_sort
|
||||
comments = unicode(self.comment.toPlainText()).strip()
|
||||
if comments:
|
||||
mi.comments = comments
|
||||
mi.series_index = int(self.series_index.value())
|
||||
if self.series.currentIndex() > -1:
|
||||
mi.series = unicode(self.series.currentText()).strip()
|
||||
tags = [t.strip() for t in unicode(self.tags.text()).strip().split(',')]
|
||||
if tags:
|
||||
mi.tags = tags
|
||||
|
||||
return mi
|
||||
|
||||
def select_cover(self):
|
||||
files = choose_images(self, 'change cover dialog',
|
||||
_('Choose cover for ') + unicode(self.title.text()))
|
||||
if not files:
|
||||
return
|
||||
_file = files[0]
|
||||
if _file:
|
||||
_file = os.path.abspath(_file)
|
||||
if not os.access(_file, os.R_OK):
|
||||
d = error_dialog(self.window, _('Cannot read'),
|
||||
_('You do not have permission to read the file: ') + _file)
|
||||
d.exec_()
|
||||
return
|
||||
cf, cover = None, None
|
||||
try:
|
||||
cf = open(_file, "rb")
|
||||
cover = cf.read()
|
||||
except IOError, e:
|
||||
d = error_dialog(self.window, _('Error reading file'),
|
||||
_("<p>There was an error reading from file: <br /><b>") + _file + "</b></p><br />"+str(e))
|
||||
d.exec_()
|
||||
if cover:
|
||||
pix = QPixmap()
|
||||
pix.loadFromData(cover)
|
||||
if pix.isNull():
|
||||
d = error_dialog(self.window, _('Error reading file'),
|
||||
_file + _(" is not a valid picture"))
|
||||
d.exec_()
|
||||
else:
|
||||
self.cover_path.setText(_file)
|
||||
self.cover.setPixmap(pix)
|
||||
self.cover_changed = True
|
||||
self.cpixmap = pix
|
||||
|
||||
def get_recommendations(self):
|
||||
return {
|
||||
'prefer_metadata_cover':
|
||||
bool(self.opt_prefer_metadata_cover.isChecked()),
|
||||
}
|
||||
|
||||
|
||||
def commit(self, save_defaults=False):
|
||||
'''
|
||||
Settings are stored in two attributes: `opf_file` and `cover_file`.
|
||||
Both may be None. Also returns a recommendation dictionary.
|
||||
'''
|
||||
recs = self.commit_options(save_defaults)
|
||||
self.user_mi = self.get_metadata()
|
||||
self.cover_file = self.opf_file = None
|
||||
if self.db is not None:
|
||||
self.db.set_metadata(self.book_id, self.user_mi)
|
||||
self.mi = self.db.get_metadata(self.book_id, index_is_id=True)
|
||||
self.mi.application_id = uuid.uuid4()
|
||||
opf = OPFCreator(os.getcwdu(), self.mi)
|
||||
self.opf_file = PersistentTemporaryFile('.opf')
|
||||
opf.render(self.opf_file)
|
||||
self.opf_file.close()
|
||||
if self.cover_changed:
|
||||
self.db.set_cover(self.book_id, pixmap_to_data(self.cover.pixmap()))
|
||||
cover = self.db.cover(self.book_id, index_is_id=True)
|
||||
if cover:
|
||||
cf = PersistentTemporaryFile('.jpeg')
|
||||
cf.write(cover)
|
||||
cf.close()
|
||||
self.cover_file = cf
|
||||
return recs
|
||||
|
336
src/calibre/gui2/convert/metadata.ui
Normal file
336
src/calibre/gui2/convert/metadata.ui
Normal file
@ -0,0 +1,336 @@
|
||||
<ui version="4.0" >
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form" >
|
||||
<property name="geometry" >
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>500</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle" >
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout" >
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_4" >
|
||||
<property name="title" >
|
||||
<string>Book Cover</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="_2" >
|
||||
<item row="1" column="0" >
|
||||
<layout class="QVBoxLayout" name="_4" >
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5" >
|
||||
<property name="text" >
|
||||
<string>Change &cover image:</string>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>cover_path</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="_5" >
|
||||
<property name="spacing" >
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="margin" >
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="cover_path" >
|
||||
<property name="readOnly" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="cover_button" >
|
||||
<property name="toolTip" >
|
||||
<string>Browse for an image to use as the cover of this book.</string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon" >
|
||||
<iconset resource="../../../../../../calibre/gui2/images.qrc" >
|
||||
<normaloff>:/images/document_open.svg</normaloff>:/images/document_open.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QCheckBox" name="opt_prefer_metadata_cover" >
|
||||
<property name="text" >
|
||||
<string>Use cover from &source file</string>
|
||||
</property>
|
||||
<property name="checked" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0" >
|
||||
<layout class="QHBoxLayout" name="_3" >
|
||||
<item>
|
||||
<widget class="ImageView" name="cover" >
|
||||
<property name="text" >
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap" >
|
||||
<pixmap resource="../../../../../../calibre/gui2/images.qrc" >:/images/book.svg</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
<zorder>opt_prefer_metadata_cover</zorder>
|
||||
<zorder></zorder>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2" >
|
||||
<item>
|
||||
<layout class="QGridLayout" name="_7" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QLabel" name="label" >
|
||||
<property name="text" >
|
||||
<string>&Title: </string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" >
|
||||
<widget class="QLineEdit" name="title" >
|
||||
<property name="toolTip" >
|
||||
<string>Change the title of this book</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0" >
|
||||
<widget class="QLabel" name="label_2" >
|
||||
<property name="text" >
|
||||
<string>&Author(s): </string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>author</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="author" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Change the author(s) of this book. Multiple authors should be separated by an &. If the author name contains an &, use && to represent it.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
<widget class="QLabel" name="label_6" >
|
||||
<property name="text" >
|
||||
<string>Author So&rt:</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>author_sort</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="author_sort" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Change the author(s) of this book. Multiple authors should be separated by a comma</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0" >
|
||||
<widget class="QLabel" name="label_3" >
|
||||
<property name="text" >
|
||||
<string>&Publisher: </string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>publisher</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" >
|
||||
<widget class="QLineEdit" name="publisher" >
|
||||
<property name="toolTip" >
|
||||
<string>Change the publisher of this book</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0" >
|
||||
<widget class="QLabel" name="label_4" >
|
||||
<property name="text" >
|
||||
<string>Ta&gs: </string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>tags</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" >
|
||||
<widget class="QLineEdit" name="tags" >
|
||||
<property name="toolTip" >
|
||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" >
|
||||
<widget class="QLabel" name="label_7" >
|
||||
<property name="text" >
|
||||
<string>&Series:</string>
|
||||
</property>
|
||||
<property name="textFormat" >
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy" >
|
||||
<cstring>series</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" >
|
||||
<widget class="QComboBox" name="series" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
|
||||
<horstretch>10</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>List of known series. You can add new series.</string>
|
||||
</property>
|
||||
<property name="whatsThis" >
|
||||
<string>List of known series. You can add new series.</string>
|
||||
</property>
|
||||
<property name="editable" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="insertPolicy" >
|
||||
<enum>QComboBox::InsertAlphabetically</enum>
|
||||
</property>
|
||||
<property name="sizeAdjustPolicy" >
|
||||
<enum>QComboBox::AdjustToContents</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" >
|
||||
<widget class="QSpinBox" name="series_index" >
|
||||
<property name="enabled" >
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip" >
|
||||
<string>Series index.</string>
|
||||
</property>
|
||||
<property name="whatsThis" >
|
||||
<string>Series index.</string>
|
||||
</property>
|
||||
<property name="prefix" >
|
||||
<string>Book </string>
|
||||
</property>
|
||||
<property name="minimum" >
|
||||
<number>1</number>
|
||||
</property>
|
||||
<property name="maximum" >
|
||||
<number>10000</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2" >
|
||||
<property name="sizePolicy" >
|
||||
<sizepolicy vsizetype="Minimum" hsizetype="Minimum" >
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title" >
|
||||
<string>Comments</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="_8" >
|
||||
<item row="0" column="0" >
|
||||
<widget class="QTextEdit" name="comment" >
|
||||
<property name="maximumSize" >
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>180</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ImageView</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>widgets.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../../../../../../calibre/gui2/images.qrc" />
|
||||
<include location="../images.qrc" />
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
23
src/calibre/gui2/convert/mobi_output.py
Normal file
23
src/calibre/gui2/convert/mobi_output.py
Normal file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
|
||||
from calibre.gui2.convert.mobi_output_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
|
||||
class PluginWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('MOBI Output')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'mobi_output',
|
||||
['prefer_author_sort', 'rescale_images', 'toc_title']
|
||||
)
|
||||
self.db, self.book_id = db, book_id
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
61
src/calibre/gui2/convert/mobi_output.ui
Normal file
61
src/calibre/gui2/convert/mobi_output.ui
Normal file
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Title for Table of Contents:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_toc_title</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="opt_toc_title"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_rescale_images">
|
||||
<property name="text">
|
||||
<string>Rescale images for &Palm devices</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_prefer_author_sort">
|
||||
<property name="text">
|
||||
<string>Use author &sort for author</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
74
src/calibre/gui2/convert/page_setup.py
Normal file
74
src/calibre/gui2/convert/page_setup.py
Normal file
@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
from PyQt4.Qt import Qt, QAbstractListModel, QVariant, SIGNAL
|
||||
|
||||
from calibre.gui2.convert.page_setup_ui import Ui_Form
|
||||
from calibre.gui2.convert import Widget
|
||||
from calibre.gui2 import NONE
|
||||
from calibre.customize.ui import input_profiles, output_profiles
|
||||
|
||||
class ProfileModel(QAbstractListModel):
|
||||
|
||||
def __init__(self, profiles):
|
||||
QAbstractListModel.__init__(self)
|
||||
self.profiles = list(profiles)
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.profiles)
|
||||
|
||||
def data(self, index, role):
|
||||
profile = self.profiles[index.row()]
|
||||
if role == Qt.DisplayRole:
|
||||
return QVariant(profile.name)
|
||||
if role in (Qt.ToolTipRole, Qt.StatusTipRole, Qt.WhatsThisRole):
|
||||
return QVariant(profile.description)
|
||||
return NONE
|
||||
|
||||
class PageSetupWidget(Widget, Ui_Form):
|
||||
|
||||
TITLE = _('Page Setup')
|
||||
|
||||
def __init__(self, parent, get_option, get_help, db=None, book_id=None):
|
||||
Widget.__init__(self, parent, 'lrf_output',
|
||||
['margin_top', 'margin_left', 'margin_right', 'margin_bottom',
|
||||
'input_profile', 'output_profile']
|
||||
)
|
||||
|
||||
self.db, self.book_id = db, book_id
|
||||
self.input_model = ProfileModel(input_profiles())
|
||||
self.output_model = ProfileModel(output_profiles())
|
||||
self.opt_input_profile.setModel(self.input_model)
|
||||
self.opt_output_profile.setModel(self.output_model)
|
||||
for x in (self.opt_input_profile, self.opt_output_profile):
|
||||
x.setMouseTracking(True)
|
||||
self.connect(x, SIGNAL('entered(QModelIndex)'), self.show_desc)
|
||||
self.initialize_options(get_option, get_help, db, book_id)
|
||||
|
||||
def show_desc(self, index):
|
||||
desc = index.model().data(index, Qt.StatusTipRole).toString()
|
||||
self.profile_description.setText(desc)
|
||||
|
||||
def set_value_handler(self, g, val):
|
||||
if g in (self.opt_input_profile, self.opt_output_profile):
|
||||
g.clearSelection()
|
||||
for idx, p in enumerate(g.model().profiles):
|
||||
if p.short_name == val:
|
||||
break
|
||||
idx = g.model().index(idx)
|
||||
sm = g.selectionModel()
|
||||
g.setCurrentIndex(idx)
|
||||
sm.select(idx, sm.SelectCurrent)
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_value_handler(self, g):
|
||||
if g in (self.opt_input_profile, self.opt_output_profile):
|
||||
idx = g.currentIndex().row()
|
||||
return g.model().profiles[idx].short_name
|
||||
return Widget.get_value_handler(self, g)
|
189
src/calibre/gui2/convert/page_setup.ui
Normal file
189
src/calibre/gui2/convert/page_setup.ui
Normal file
@ -0,0 +1,189 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>572</width>
|
||||
<height>476</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Output profile:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_output_profile</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="opt_output_profile"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>4</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Profile description</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="profile_description">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>4</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&Input profile:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_input_profile</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QListView" name="opt_input_profile"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>4</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Margins</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Left:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_margin_left</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QDoubleSpinBox" name="opt_margin_left">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>&Top:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_margin_top</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QDoubleSpinBox" name="opt_margin_top">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string>&Right:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_margin_right</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QDoubleSpinBox" name="opt_margin_right">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Bottom:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>opt_margin_bottom</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDoubleSpinBox" name="opt_margin_bottom">
|
||||
<property name="suffix">
|
||||
<string> pt</string>
|
||||
</property>
|
||||
<property name="decimals">
|
||||
<number>1</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
244
src/calibre/gui2/convert/single.py
Normal file
244
src/calibre/gui2/convert/single.py
Normal file
@ -0,0 +1,244 @@
|
||||
#!/usr/bin/env python
|
||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
||||
from __future__ import with_statement
|
||||
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2009, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import sys, cPickle
|
||||
|
||||
from PyQt4.Qt import QString, SIGNAL, QAbstractListModel, Qt, QVariant, QFont
|
||||
|
||||
from calibre.gui2 import ResizableDialog, NONE
|
||||
from calibre.gui2.convert import GuiRecommendations, save_specifics, \
|
||||
load_specifics
|
||||
from calibre.gui2.convert.single_ui import Ui_Dialog
|
||||
from calibre.gui2.convert.metadata import MetadataWidget
|
||||
from calibre.gui2.convert.look_and_feel import LookAndFeelWidget
|
||||
from calibre.gui2.convert.page_setup import PageSetupWidget
|
||||
|
||||
from calibre.ebooks.conversion.plumber import Plumber, supported_input_formats, \
|
||||
INPUT_FORMAT_PREFERENCES, OUTPUT_FORMAT_PREFERENCES
|
||||
from calibre.customize.ui import available_output_formats
|
||||
from calibre.customize.conversion import OptionRecommendation
|
||||
from calibre.utils.logging import Log
|
||||
|
||||
class NoSupportedInputFormats(Exception):
|
||||
pass
|
||||
|
||||
def sort_formats_by_preference(formats, prefs):
|
||||
def fcmp(x, y):
|
||||
try:
|
||||
x = prefs.index(x)
|
||||
except ValueError:
|
||||
x = sys.maxint
|
||||
try:
|
||||
y = prefs.index(y)
|
||||
except ValueError:
|
||||
y = sys.maxint
|
||||
return cmp(x, y)
|
||||
return sorted(formats, cmp=fcmp)
|
||||
|
||||
class GroupModel(QAbstractListModel):
|
||||
|
||||
def __init__(self, widgets):
|
||||
self.widgets = widgets
|
||||
QAbstractListModel.__init__(self)
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.widgets)
|
||||
|
||||
def data(self, index, role):
|
||||
try:
|
||||
widget = self.widgets[index.row()]
|
||||
except:
|
||||
return NONE
|
||||
if role == Qt.DisplayRole:
|
||||
return QVariant(widget.config_title())
|
||||
if role == Qt.DecorationRole:
|
||||
return QVariant(widget.config_icon())
|
||||
if role == Qt.FontRole:
|
||||
f = QFont()
|
||||
f.setBold(True)
|
||||
return QVariant(f)
|
||||
return NONE
|
||||
|
||||
class Config(ResizableDialog, Ui_Dialog):
|
||||
'''
|
||||
Configuration dialog for single book conversion. If accepted, has the
|
||||
following important attributes
|
||||
|
||||
input_path - Path to input file
|
||||
output_format - Output format (without a leading .)
|
||||
input_format - Input format (without a leading .)
|
||||
opf_path - Path to OPF file with user specified metadata
|
||||
cover_path - Path to user specified cover (can be None)
|
||||
recommendations - A pickled list of 3 tuples in the same format as the
|
||||
recommendations member of the Input/Output plugins.
|
||||
'''
|
||||
|
||||
def __init__(self, parent, db, book_id,
|
||||
preferred_input_format=None, preferred_output_format=None):
|
||||
ResizableDialog.__init__(self, parent)
|
||||
|
||||
if preferred_input_format is None and db is not None:
|
||||
recs = load_specifics(db, book_id)
|
||||
if recs:
|
||||
preferred_input_format = recs.get('gui_preferred_input_format',
|
||||
None)
|
||||
|
||||
self.setup_input_output_formats(db, book_id, preferred_input_format,
|
||||
preferred_input_format)
|
||||
self.db, self.book_id = db, book_id
|
||||
self.setup_pipeline()
|
||||
|
||||
self.connect(self.input_formats, SIGNAL('currentIndexChanged(QString)'),
|
||||
self.setup_pipeline)
|
||||
self.connect(self.output_formats, SIGNAL('currentIndexChanged(QString)'),
|
||||
self.setup_pipeline)
|
||||
self.connect(self.groups, SIGNAL('activated(QModelIndex)'),
|
||||
self.show_pane)
|
||||
self.connect(self.groups, SIGNAL('clicked(QModelIndex)'),
|
||||
self.show_pane)
|
||||
self.connect(self.groups, SIGNAL('entered(QModelIndex)'),
|
||||
self.show_group_help)
|
||||
self.groups.setMouseTracking(True)
|
||||
|
||||
@property
|
||||
def input_format(self):
|
||||
return unicode(self.input_formats.currentText()).lower()
|
||||
|
||||
@property
|
||||
def output_format(self):
|
||||
return unicode(self.output_formats.currentText()).lower()
|
||||
|
||||
|
||||
def setup_pipeline(self, *args):
|
||||
input_format = self.input_format
|
||||
output_format = self.output_format
|
||||
input_path = self.db.format_abspath(self.book_id, input_format,
|
||||
index_is_id=True)
|
||||
self.input_path = input_path
|
||||
output_path = 'dummy.'+output_format
|
||||
log = Log()
|
||||
log.outputs = []
|
||||
self.plumber = Plumber(input_path, output_path, log)
|
||||
|
||||
def widget_factory(cls):
|
||||
return cls(self.stack, self.plumber.get_option_by_name,
|
||||
self.plumber.get_option_help, self.db, self.book_id)
|
||||
|
||||
|
||||
self.mw = widget_factory(MetadataWidget)
|
||||
self.setWindowTitle(_('Convert')+ ' ' + unicode(self.mw.title.text()))
|
||||
lf = widget_factory(LookAndFeelWidget)
|
||||
ps = widget_factory(PageSetupWidget)
|
||||
|
||||
output_widget = None
|
||||
name = self.plumber.output_plugin.name.lower().replace(' ', '_')
|
||||
try:
|
||||
output_widget = __import__(name)
|
||||
pw = output_widget.PluginWidget
|
||||
pw.ICON = ':/images/back.svg'
|
||||
pw.HELP = _('Options specific to the output format.')
|
||||
output_widget = widget_factory(pw)
|
||||
except ImportError:
|
||||
pass
|
||||
input_widget = None
|
||||
name = self.plumber.input_plugin.name.lower().replace(' ', '_')
|
||||
try:
|
||||
input_widget = __import__(name)
|
||||
pw = input_widget.PluginWidget
|
||||
pw.ICON = ':/images/forward.svg'
|
||||
pw.HELP = _('Options specific to the input format.')
|
||||
input_widget = widget_factory(pw)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
while True:
|
||||
c = self.stack.currentWidget()
|
||||
if not c: break
|
||||
self.stack.removeWidget(c)
|
||||
|
||||
widgets = [self.mw, lf, ps]
|
||||
if input_widget is not None:
|
||||
widgets.append(input_widget)
|
||||
if output_widget is not None:
|
||||
widgets.append(output_widget)
|
||||
for w in widgets:
|
||||
self.stack.addWidget(w)
|
||||
self.connect(w, SIGNAL('set_help(PyQt_PyObject)'),
|
||||
self.help.setPlainText)
|
||||
|
||||
self._groups_model = GroupModel(widgets)
|
||||
self.groups.setModel(self._groups_model)
|
||||
|
||||
self.groups.setCurrentIndex(self._groups_model.index(0))
|
||||
|
||||
|
||||
def setup_input_output_formats(self, db, book_id, preferred_input_format,
|
||||
preferred_output_format):
|
||||
available_formats = db.formats(book_id, index_is_id=True)
|
||||
if not available_formats:
|
||||
available_formats = ''
|
||||
available_formats = set([x.lower() for x in
|
||||
available_formats.split(',')])
|
||||
input_formats = set([x.lower() for x in supported_input_formats()])
|
||||
input_formats = \
|
||||
sorted(available_formats.intersection(input_formats))
|
||||
if not input_formats:
|
||||
raise NoSupportedInputFormats
|
||||
output_formats = sorted(available_output_formats())
|
||||
output_formats.remove('oeb')
|
||||
preferred_input_format = preferred_input_format if \
|
||||
preferred_input_format in input_formats else \
|
||||
sort_formats_by_preference(input_formats,
|
||||
INPUT_FORMAT_PREFERENCES)[0]
|
||||
preferred_output_format = preferred_output_format if \
|
||||
preferred_output_format in output_formats else \
|
||||
sort_formats_by_preference(output_formats,
|
||||
OUTPUT_FORMAT_PREFERENCES)[0]
|
||||
self.input_formats.addItems(list(map(QString, [x.upper() for x in
|
||||
input_formats])))
|
||||
self.output_formats.addItems(list(map(QString, [x.upper() for x in
|
||||
output_formats])))
|
||||
self.input_formats.setCurrentIndex(input_formats.index(preferred_input_format))
|
||||
self.output_formats.setCurrentIndex(output_formats.index(preferred_output_format))
|
||||
|
||||
def show_pane(self, index):
|
||||
self.stack.setCurrentIndex(index.row())
|
||||
|
||||
def accept(self):
|
||||
recs = GuiRecommendations()
|
||||
for w in self._groups_model.widgets:
|
||||
x = w.commit(save_defaults=False)
|
||||
recs.update(x)
|
||||
self.opf_path, self.cover_path = self.mw.opf_file, self.mw.cover_file
|
||||
self._recommendations = recs
|
||||
if self.db is not None:
|
||||
recs['gui_preferred_input_format'] = self.input_format
|
||||
save_specifics(self.db, self.book_id, recs)
|
||||
ResizableDialog.accept(self)
|
||||
|
||||
@property
|
||||
def recommendations(self):
|
||||
recs = [(k, v, OptionRecommendation.HIGH) for k, v in
|
||||
self._recommendations.items()]
|
||||
return cPickle.dumps(recs, -1)
|
||||
|
||||
def show_group_help(self, index):
|
||||
widget = self._groups_model.widgets[index.row()]
|
||||
self.help.setPlainText(widget.HELP)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.library.database2 import LibraryDatabase2
|
||||
from calibre.gui2 import images_rc, Application
|
||||
images_rc
|
||||
a = Application([])
|
||||
db = LibraryDatabase2('/home/kovid/documents/library')
|
||||
d = Config(None, db, 594)
|
||||
d.show()
|
||||
a.exec_()
|
||||
|
200
src/calibre/gui2/convert/single.ui
Normal file
200
src/calibre/gui2/convert/single.ui
Normal file
@ -0,0 +1,200 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1024</width>
|
||||
<height>700</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="../images.qrc">
|
||||
<normaloff>:/images/convert.svg</normaloff>:/images/convert.svg</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>&Input format:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>input_formats</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="input_formats"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&Output format:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>output_formats</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="output_formats"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0" rowspan="3">
|
||||
<widget class="QListView" name="groups">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="tabKeyNavigation">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>48</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>20</number>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QScrollArea" name="scrollArea">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>4</horstretch>
|
||||
<verstretch>10</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="lineWidth">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>810</width>
|
||||
<height>492</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QStackedWidget" name="stack">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<widget class="QWidget" name="page"/>
|
||||
<widget class="QWidget" name="page_2"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QTextEdit" name="help">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>16777215</width>
|
||||
<height>130</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../images.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -285,7 +285,7 @@ class FontFamilyModel(QAbstractListModel):
|
||||
print 'WARNING: Could not load fonts'
|
||||
traceback.print_exc()
|
||||
self.families.sort()
|
||||
self.families[:0] = ['None']
|
||||
self.families[:0] = [_('None')]
|
||||
|
||||
def rowCount(self, *args):
|
||||
return len(self.families)
|
||||
|
@ -53,7 +53,7 @@ def get_opts_from_parser(parser, prefix):
|
||||
for x in do_opt(o): yield x
|
||||
|
||||
def send(ans):
|
||||
pat = re.compile('([^0-9a-zA-Z_.])')
|
||||
pat = re.compile('([^0-9a-zA-Z_./])')
|
||||
for x in sorted(set(ans)):
|
||||
x = pat.sub(lambda m : '\\'+m.group(1), x)
|
||||
if x.endswith('\\ '):
|
||||
|
4
todo
4
todo
@ -3,14 +3,12 @@
|
||||
|
||||
* Refactor IPC code to use communication logic from multiprocessing
|
||||
|
||||
* Use multiprocessing for cpu_count instead of QThread
|
||||
|
||||
* Rationalize books table. Add a pubdate column, remove the uri column (and associated support in add_books) and convert series_index to a float.
|
||||
|
||||
* Replace single application stuff with Listener from multiprocessing
|
||||
|
||||
* Refactor add books to use a separate process named calibre-worker-add
|
||||
- Dont use the process for adding a single book
|
||||
- Use a process pool for speed
|
||||
- Use a process pool for speed or multiple process for stability (20 per process?)
|
||||
|
||||
* Change mobi metadata setter to use author_sort setting from MOBI output plugin instead of mobi.py
|
||||
|
@ -286,7 +286,7 @@ class gui(OptionlessCommand):
|
||||
with open('images.qrc', 'wb') as f:
|
||||
f.write(manifest)
|
||||
try:
|
||||
check_call(['pyrcc4', '-o', images, 'images.qrc'])
|
||||
check_call(['pyrcc4', '-py2', '-o', images, 'images.qrc'])
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
@ -399,7 +399,7 @@ class update(OptionlessCommand):
|
||||
' a version update.'
|
||||
|
||||
def run(self):
|
||||
for x in ['build', 'dist', 'docs'] + \
|
||||
for x in ['build', 'dist'] + \
|
||||
glob.glob(os.path.join('src', 'calibre', 'plugins', '*')):
|
||||
if os.path.exists(x):
|
||||
if os.path.isdir(x):
|
||||
|
Loading…
x
Reference in New Issue
Block a user