当前位置: 首页 > 工具软件 > Calibre > 使用案例 >

Calibre解析

朱炳
2023-12-01

在转换chm、markdown、epub等文件中遇到大量问题后,我决定仔读一遍Calibre用户手册,并结合代码分析。
注意:本次我会使用chrome自带的翻译功能。

首先放上我另一篇文章开源软件calibre源码研究与程序编译(编译只能在linux下进行,未成功)

在这篇里,我讨论了如何设置Calibre的开发环境。

这里,再次,首先下载源码包。这次我使用了git,所以为了以防万一,我切换版本到4.12.0。

经过一番配置,我再次获得了Hello World。

代码布局

所有口径的python代码都在calibre软件包中。该软件包包含以下主要子软件包

设备-所有设备驱动程序。只需查看一些内置驱动程序即可了解它们的工作原理。

有关详细信息,请参见:devices.interface定义了设备驱动程序支持的接口,devices.usbms并定义了连接到USBMS设备的通用驱动程序。口径中所有基于USBMS的驱动程序均继承自该驱动程序。
电子书-所有电子书转换/元数据代码。一个很好的起点是calibre.ebooks.conversion.cli为电子书转换命令提供动力的模块。转换过程通过进行控制conversion.plumber。格式无关的代码都在中ebooks.oeb,而格式相关的代码在中ebooks.format_name。

读取,写入和下载元数据全在其中 ebooks.metadata
转换发生在管道中,有关管道的结构,请参见简介。管道包括输入插件,各种转换和输出插件。构造和驱动管道的代码在中plumber.py。管道可处理像未压缩的epub之类的电子书,具有清单,书脊,目录,指南,html内容等。管理此表示的类是OEBBook in ebooks.oeb.base。转换过程中应用于该书的各种转换都存在于中oeb/transforms/*.py。输入和输出插件都位于中conversion/plugins/*.py。
电子书编辑使用其他容器对象进行。电子书编辑工具的API文档中对此进行了记录。
db-数据库后端。有关口径库的接口,请参见数据库接口的API文档。

内容服务器:srv是口径的内容服务器。

gui2-图形用户界面。GUI初始化发生在gui2.main和中gui2.ui。电子书阅读器在中gui2.viewer。电子书编辑器在中gui2.tweak_book。

如果要查找所有各种口径可执行文件的入口点,请查看linux.py中的entry_points结构。

如果您需要帮助来理解代码,请在开发论坛中发帖 ,您很可能会从calibre的众多开发人员之一获得帮助。

我又试了试使用交互式Python解释器
这里我出了一点问题。

from calibre import ipython
ipython(locals())

直接添加在最开始无效
但是仔细研究之后,我们是在calibre这个包的__init__.py,ipython是定义在这个文件里的一个函数。
所以调用以上语句,只需要移到最后即可以生效。
并且在这个文件里,其实直接调用ipython(locals())就可以了。
但是调用了之后,大家会发现,只是进入了一个shell。并不是ipython
为何呢?
在查询论坛之后,我找的这样一个帖子:
ipython somehow not working?
按里面的说法,calibre已经不再包含ipython
使用相应代码是直接进入python shell。当然也可以用。
但是如果想要使用ipython的话,应该怎么做那?
首先,假如你电脑上没有python2.7的话,下载python2.7
然后pip install ipython
安装好了以后,使用ipython2命令打开ipython 5.9
这是python2下支持的最高版本。
输入

import sys
print sys.path

可以得到如下类型的内容

['', 'D:\\Program Files\\Python\\Python27\\Scripts\\ipython2.exe', ...]

import sys
sys.path.extend([the path you got])
import IPython
from IPython.config.loader import Config
IPython.embed(user_ns=locals())

输入之前

from calibre import ipython
ipython(locals())

的位置就可以运行ipython。
从方便的角度看,我建议修改ipyhon函数。

def ipython(user_ns=None):
    import sys
    sys.path.extend([the path you got])
    import IPython
    from IPython.config.loader import Config
    # and then put to a suitable place in tour code this: 
    IPython.embed(user_ns=user_ns)
    '''
    from calibre.utils.ipython import ipython
    ipython(user_ns=user_ns)
    '''

如上,这样就可以依然使用

from calibre import ipython
ipython(locals())

来使用ipython了。
但是我们要来看一看,这样用,与它的

    from calibre.utils.ipython import ipython
    ipython(user_ns=user_ns)

有什么区别呢?
它调用了calibre.utils.ipython,我们来看一看。
这个文件在源码包的calibre\utils\ipython.py里。

def ipython(user_ns=None):
    os.environ['IPYTHONDIR'] = ipydir
    try:
        from IPython.terminal.embed import InteractiveShellEmbed
        from traitlets.config.loader import Config
        from IPython.terminal.prompts import Prompts, Token
    except ImportError:
        return simple_repl(user_ns=user_ns)

    class CustomPrompt(Prompts):

        def in_prompt_tokens(self, cli=None):
            return [
                (Token.Prompt, 'calibre['),
                (Token.PromptNum, get_version()),
                (Token.Prompt, ']> '),
            ]

        def out_prompt_tokens(self):
            return []

    defns = {'os':os, 're':re, 'sys':sys}
    defns.update(user_ns or {})

    c = Config()
    user_conf = os.path.expanduser('~/.ipython/profile_default/ipython_config.py')
    if os.path.exists(user_conf):
        exec_path(user_conf, {'get_config': lambda: c})
    c.TerminalInteractiveShell.prompts_class = CustomPrompt
    c.InteractiveShellApp.exec_lines = [
        'from __future__ import division, absolute_import, unicode_literals, print_function',
        ]
    c.TerminalInteractiveShell.confirm_exit = False
    c.TerminalInteractiveShell.banner1 = BANNER
    c.BaseIPythonApplication.ipython_dir = ipydir

    c.InteractiveShell.separate_in = ''
    c.InteractiveShell.separate_out = ''
    c.InteractiveShell.separate_out2 = ''

    ipshell = InteractiveShellEmbed.instance(config=c, user_ns=user_ns)
    ipshell()

函数如上

老实说我看不懂【摊手】

不过没事,可以debug就行。
首先检查走的是那条路。
啊,不幸的是,走的是

    except ImportError:
        return simple_repl(user_ns=user_ns)

那么一定前面出错了,检查一下。

    try:
        from IPython.terminal.embed import InteractiveShellEmbed
        from traitlets.config.loader import Config
        from IPython.terminal.prompts import Prompts, Token

这里,全部导入失败了。
然而测试了一下,这些都能导入的啊?问题在哪里呢?
测试了一遍,无法导入ipython。
那么把sys.path.extend([the path you got])加到这个文件开头。
问题解决了!
现在,我们可以用ipython了。

 类似资料: