This commit is contained in:
douboer 2025-08-15 13:49:02 +08:00
parent 8873c70a60
commit 0bc6844209
76 changed files with 726 additions and 11197 deletions

BIN
.DS_Store vendored

Binary file not shown.

23
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
"python.defaultInterpreterPath": "/Users/gavin/venv/bin/python",
"python-envs.pythonProjects": [
{
"path": "/Users/gavin/venv",
}
],
// Python
//
"python.terminal.activateEnvironment": true,
// VS Code 使 shell
// source venv使 Python
"terminal.integrated.profiles.osx": {
"zsh_with_venv": {
"path": "/bin/zsh",
"args": ["-c", "source ~/venv/bin/activate; exec zsh"]
}
},
"terminal.integrated.defaultProfile.osx": "zsh_with_venv",
]
}

Binary file not shown.

View File

@ -6,12 +6,15 @@ annotationdata.py
- 提供parse_location辅助函数解析笔记定位信息 - 提供parse_location辅助函数解析笔记定位信息
- 返回结构化的annotations数据便于后续章节定位与导出 - 返回结构化的annotations数据便于后续章节定位与导出
依赖config.py 统一管理路径和配置项
主要接口 主要接口
- get_annotations(db_path, bookid=None)返回所有或指定assetid的笔记结构为{assetid: {uuid: {...}}} - get_annotations(db_path, bookid=None)返回所有或指定assetid的笔记结构为{assetid: {uuid: {...}}}
- parse_location(location)解析ZANNOTATIONLOCATION返回(idref, filepos) - parse_location(location)解析ZANNOTATIONLOCATION返回(idref, filepos)
依赖sqlite3, collections, re, os, datetime 依赖sqlite3, collections, re, os, datetime
""" """
import config
import sqlite3 import sqlite3
from collections import defaultdict from collections import defaultdict
@ -34,7 +37,7 @@ def parse_location(location):
filepos = matches[1] if len(matches) > 1 else None filepos = matches[1] if len(matches) > 1 else None
return idref, filepos return idref, filepos
def get_annotations(db_path='./data/AEAnnotation.sqlite', bookid=None): def get_annotations(db_path=config.LOCAL_ANNOTATION_DB, bookid=None):
# 检查WAL模式相关文件 # 检查WAL模式相关文件
base = db_path.rsplit('.', 1)[0] base = db_path.rsplit('.', 1)[0]
wal_path = base + '.sqlite-wal' wal_path = base + '.sqlite-wal'

View File

@ -5,6 +5,8 @@ booklist_parse.py
- 解析iBooks的Books.plist提取所有书籍元数据书名作者路径时间等 - 解析iBooks的Books.plist提取所有书籍元数据书名作者路径时间等
- 解析BKLibrary.sqlite获取每本书的最近打开时间苹果时间戳基准2001-01-01 - 解析BKLibrary.sqlite获取每本书的最近打开时间苹果时间戳基准2001-01-01
依赖config.py 统一管理路径和配置项
主要接口 主要接口
- parse_books_plist(plist_path)返回所有书籍元数据结构为{bk_id: {...}} - parse_books_plist(plist_path)返回所有书籍元数据结构为{bk_id: {...}}
- get_books_last_open(db_path)返回所有书籍最近打开时间结构为{bk_id: {'last_open': 时间戳}} - get_books_last_open(db_path)返回所有书籍最近打开时间结构为{bk_id: {'last_open': 时间戳}}
@ -12,13 +14,14 @@ booklist_parse.py
依赖plistlib, collections, sqlite3, os, datetime 依赖plistlib, collections, sqlite3, os, datetime
典型用法 典型用法
booksinfo = parse_books_plist('./data/Books.plist') booksinfo = parse_books_plist(config.LOCAL_BOOKS_PLIST)
books_open = get_books_last_open('data/BKLibrary.sqlite') books_open = get_books_last_open(config.LOCAL_LIBRARY_DB)
""" """
import config
import plistlib import plistlib
from collections import defaultdict from collections import defaultdict
def parse_books_plist(plist_path): def parse_books_plist(plist_path=config.LOCAL_BOOKS_PLIST):
booksinfo = defaultdict(dict) booksinfo = defaultdict(dict)
with open(plist_path, 'rb') as f: plist_data = plistlib.load(f) with open(plist_path, 'rb') as f: plist_data = plistlib.load(f)
for book in plist_data.get('Books', []): for book in plist_data.get('Books', []):
@ -38,7 +41,7 @@ def parse_books_plist(plist_path):
import sqlite3 import sqlite3
import os import os
def get_books_last_open(db_path='data/BKLibrary.sqlite'): def get_books_last_open(db_path=config.LOCAL_LIBRARY_DB):
""" """
从BKLibrary.sqlite获取书籍最近打开时间 从BKLibrary.sqlite获取书籍最近打开时间
返回defaultdict(dict)bk_id为索引包含最近打开时间 返回defaultdict(dict)bk_id为索引包含最近打开时间
@ -51,8 +54,7 @@ def get_books_last_open(db_path='data/BKLibrary.sqlite'):
conn = sqlite3.connect(db_path) conn = sqlite3.connect(db_path)
cursor = conn.cursor() cursor = conn.cursor()
# ZBKLIBRARYASSET表包含书籍信息 # ZBKLIBRARYASSET表包含书籍信息
cursor.execute(''' SELECT ZASSETID, zlastopendate FROM ZBKLIBRARYASSET WHERE zlastopendate IS NOT NULL cursor.execute(''' SELECT ZASSETID, zlastopendate FROM ZBKLIBRARYASSET WHERE zlastopendate IS NOT NULL ''')
''')
rows = cursor.fetchall() rows = cursor.fetchall()
for row in rows: for row in rows:
asset_id, last_open = row asset_id, last_open = row
@ -67,7 +69,7 @@ def get_books_last_open(db_path='data/BKLibrary.sqlite'):
return books_open return books_open
if __name__ == '__main__': if __name__ == '__main__':
booksinfo = parse_books_plist('./data/Books.plist') booksinfo = parse_books_plist(config.LOCAL_BOOKS_PLIST)
from pprint import pprint from pprint import pprint
print("\n【前三条示例】") print("\n【前三条示例】")
for k, v in list(booksinfo.items())[:3]: for k, v in list(booksinfo.items())[:3]:

29
config.py Normal file
View File

@ -0,0 +1,29 @@
"""
config.py
---------
统一管理所有涉及文件路径目录常量等配置项
"""
import os
# 数据目录
DATA_DIR = os.path.join(os.path.dirname(__file__), 'data')
EXPORT_NOTES_DIR = os.path.join(os.path.dirname(__file__), 'export_notes')
EXAMPLES_DIR = os.path.join(os.path.dirname(__file__), 'examples')
# iBooks 源数据路径(可根据实际环境修改)
IBOOKS_ANNOTATION_DB = os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite')
IBOOKS_ANNOTATION_SHM = IBOOKS_ANNOTATION_DB + '-shm'
IBOOKS_ANNOTATION_WAL = IBOOKS_ANNOTATION_DB + '-wal'
IBOOKS_LIBRARY_DB = os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite')
IBOOKS_BOOKS_PLIST = os.path.expanduser('~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist')
# 本地数据文件名
LOCAL_ANNOTATION_DB = os.path.join(DATA_DIR, 'AEAnnotation.sqlite')
LOCAL_ANNOTATION_SHM = os.path.join(DATA_DIR, 'AEAnnotation.sqlite-shm')
LOCAL_ANNOTATION_WAL = os.path.join(DATA_DIR, 'AEAnnotation.sqlite-wal')
LOCAL_LIBRARY_DB = os.path.join(DATA_DIR, 'BKLibrary.sqlite')
LOCAL_BOOKS_PLIST = os.path.join(DATA_DIR, 'Books.plist')
# 其他可扩展配置项
# ...

Binary file not shown.

Binary file not shown.

View File

@ -155,14 +155,37 @@ answer = inquirer.fuzzy(
--- ---
### 9.1 主要代码文件说明
- `exportbooknotes.py`:主程序,负责数据同步、交互式选择、笔记导出等全部流程,菜单排序和显示基于最近打开时间戳。 ## 9.1 主要代码文件说明(细化)
- `annotationdata.py`:负责从 iBooks 的 SQLite 数据库中解析和获取笔记数据。
- `booklist_parse.py`:负责解析 Books.plist获取书籍元数据如书名、路径等并提供 get_books_last_open 读取 BKLibrary.sqlite 获取最近打开时间。 - `exportbooknotes.py`
- `opf_parse.py`:负责解析 epub 的 OPF 文件,获取章节与文件映射关系。 - 数据同步:自动复制 iBooks 数据库和元数据到本地。
- `toc_parse.py`:负责解析 NCX 目录文件,章节定位,以及辅助章节查找。 - 菜单交互:按最近打开时间戳排序,显示“书名 [时间戳]”,支持模糊搜索。
- `backup/booksnote.py`:备份或历史版本的笔记处理脚本。 - 只处理用户选中书籍的笔记,按章节分组导出 Markdown。
- 依赖核心解析模块,负责主流程调度。
- `annotationdata.py`
- 解析 AEAnnotation.sqlite提取所有或指定 assetid 的笔记。
- 支持苹果时间戳转换,结构化输出。
- parse_location 辅助函数,统一解析笔记定位信息。
- `booklist_parse.py`
- 解析 Books.plist获取书籍元数据书名、作者、路径、时间等
- 解析 BKLibrary.sqlite获取每本书的最近打开时间zlastopendate苹果时间戳
- 提供统一数据接口,便于主流程排序和展示。
- `opf_parse.py`
- 解析 epub 的 OPF 文件获取章节与文件映射关系idref -> href
- 支持多种 epub 目录结构。
- `toc_parse.py`
- 解析 NCX 目录文件,递归构建章节树结构。
- find_label_path支持通过 ref 和 filepos 查找完整 label 路径。
- find_section_by_selectedtext通过选中文本在 html 文件中定位章节标题。
- parse_html_title解析 html 文件标题。
- `backup/booksnote.py`
- 历史/备份脚本,辅助数据迁移或格式转换。
--- ---

View File

@ -1,4 +1,4 @@
# 笔记导出 2025-08-12 21:16 # 笔记导出 2025-08-15 13:25
## 传统十论 ## 传统十论
@ -530,6 +530,11 @@
如今人必称法治的时代,用韩非式的“法治”偷换现代宪政法治的危险也是不容忽视的,我们应当小心不要落入黄宗羲揭示的那个陷阱: 如今人必称法治的时代,用韩非式的“法治”偷换现代宪政法治的危险也是不容忽视的,我们应当小心不要落入黄宗羲揭示的那个陷阱:
“法愈密而天下之乱即生于法之中,所谓非法之法也。” “法愈密而天下之乱即生于法之中,所谓非法之法也。”
那种把清亡后出现混乱局面简单归结为“西化”与“激进”所致的看法是肤浅的:如果清亡后的混乱是因为西化,那以前的历代王朝灭亡时产生的混乱又是为何?换言之,清亡后的乱世究竟有几分是现代化“欲速则不达”的结果,几分只是“治乱循环”传统怪圈的一环?
黄宗羲的批判虽然激烈而且深刻,他的“建构”却不能说是成功的。他鼓吹的“学校政治”并不包含程序民主,只是高度泛道德化的政治
,而现在人们都知道这样的政治容易走向“道德专政”
### 西儒会融,解构“法道互补”——典籍与行为中的文化史悖论及中国现代化之路 / 绪论:“儒”与“吏 ### 西儒会融,解构“法道互补”——典籍与行为中的文化史悖论及中国现代化之路 / 绪论:“儒”与“吏
官是掌权的,这时主要从科举“正途”出身。吏是办事的,这时或从民间作为一种职役征调而来,或者由官“自辟僚属”而选用,前者多为奔走执事者,如皂隶、里胥、门子、捕快,后者多为文案工作者,称为文吏或书吏。但不论职役还是文吏,地位都低于官,有的王朝甚至规定吏户入贱籍,法定地位还低于一般民(农)户。然而我国传统制度的一个特点就是“县官不如现管”、狐假虎威的“奴隶”比无威可恃的“自由民”更有优势,因此胥吏的实际势力是很大的。 官是掌权的,这时主要从科举“正途”出身。吏是办事的,这时或从民间作为一种职役征调而来,或者由官“自辟僚属”而选用,前者多为奔走执事者,如皂隶、里胥、门子、捕快,后者多为文案工作者,称为文吏或书吏。但不论职役还是文吏,地位都低于官,有的王朝甚至规定吏户入贱籍,法定地位还低于一般民(农)户。然而我国传统制度的一个特点就是“县官不如现管”、狐假虎威的“奴隶”比无威可恃的“自由民”更有优势,因此胥吏的实际势力是很大的。
@ -562,3 +567,142 @@
强者对弱者“无为”,可以理解为宽容,弱者对强者“无为”,就沦于苟且了。权力对权利“无为”意味着自由,而权利对权力“无为”则意味着奴役 强者对弱者“无为”,可以理解为宽容,弱者对强者“无为”,就沦于苟且了。权力对权利“无为”意味着自由,而权利对权力“无为”则意味着奴役
而道家的上述诡辩论则为本来难以相容的“儒表”与“法里”提供了关键性的黏合剂,为逻辑上摩擦剧烈的王道之表与霸道之里加注了有效的润滑油:法家指鹿为马,儒家曰此非马,则被坑矣;曰此马也,则非儒矣。而庄子曰:马亦鹿也,鹿亦马也,所谓“万物一齐”也。是故指鹿为鹿者,儒也;而指鹿为马者,尤大儒也。言“大”者何?谓其超越是非之俗见,是为“真人”、“至人”也。故曰:法家儒也,儒家法也。而儒表法里者,其旷世之大儒乎!庄周的逻辑适足以论证如此“高尚的无耻”! 而道家的上述诡辩论则为本来难以相容的“儒表”与“法里”提供了关键性的黏合剂,为逻辑上摩擦剧烈的王道之表与霸道之里加注了有效的润滑油:法家指鹿为马,儒家曰此非马,则被坑矣;曰此马也,则非儒矣。而庄子曰:马亦鹿也,鹿亦马也,所谓“万物一齐”也。是故指鹿为鹿者,儒也;而指鹿为马者,尤大儒也。言“大”者何?谓其超越是非之俗见,是为“真人”、“至人”也。故曰:法家儒也,儒家法也。而儒表法里者,其旷世之大儒乎!庄周的逻辑适足以论证如此“高尚的无耻”!
即便我们写不出罗尔斯、哈耶克那种层次的理论巨著,我们也可以实行“拿来主义”,但倘若我们干不了甘地、哈维尔等人所干之事,那是绝不会有人代替我们干的
达则独善其身”就是说大权在握时尤其要注意权力的自律,而不能凭借权力用自己哪怕是真诚的理想去无限制地律人
如果无权者即“穷”者中没有人以自我牺牲的精神“兼济天下”,则所有的人都将难以“独善其身”。
至于“穷则独善其身”,其缺陷在于只强调无权者的道德自律,而没有考虑需要争取和维护“无权者的权利”
圣雄而达,则高于圣君,因为后者如果“己所欲必施于人”是会异化成暴君的。圣雄而穷,则高于圣隐,因为后者如果只是“知其不可而不为”,则不过犬儒而已。而圣雄者,穷则兼济天下,知其不可而为之,人所不欲之牺牲而施诸己,岂止“己所不欲勿施于人”哉!达则独善其身,己所欲而必请于人然后施之天下,真所谓“大道之行天下为公”矣
> 这个很反人性,因为你要达者不做恶,他要穷者坚持底线。所谓穷则志短。
而为了约束权力,对“达”者的舆论监督实际上实行的是“有错推定”原则,“达”者必须承担无错举证责任,如不能证明你无错,那你就被视为有错。这不就是“达则独善其身”吗
如果“穷”者中多一些“兼济天下”的圣雄精神,那就能“以我之大公争得天下人之小私”,而实现“因民之所利而利之”的圣贤之道。如果对“达”者多一点约束圣君之制使其“独善其身”,那就会消除“以我之大私为天下之大公”的千年祸患,真正实现“克己复礼,天下归仁”。因此我们应该让“穷”者多一点权利意识,而“达”者少一点权力迷信。
> 作者太高估了人性
穷”者要能够“有为而有不为”,“达”者要善于“无为而无不为”。只有这样,我们这个文明古国才能跳出因“达则有为穷则无为”而陷入“法道互补”的怪圈,儒学本身才能摆脱“儒表法里”与“儒表道里”的双重异化、抵抗强权哲学与犬儒哲学的两面夹击,才有可能实现“老内圣开出新外王”
### 谁,面向哪个东方?——评弗兰克:《重新面向东方》,兼论所谓“西方中心论”问题
有趣的是清代关税税率比更为看重市舶之利的两宋为低,但却比根本无所谓关税
用布罗代尔的话说:
西欧贵金属也经由波罗的海流向东欧。这些落后国家为西方提供小麦、木材、黑麦、鱼、皮革、毛皮但很少购买西方的商品。实际上是西方逐渐促成这些国家的货币流通。16世纪与俄罗斯纳尔瓦的贸易便是一例……1553年英国人在俄罗斯白海港口阿尔汉格尔斯克开创的贸易是又一个例子。18世纪圣彼得堡的贸易也属于这种情况。必须注入外国货币才能指望俄国输出西方期待的原料。荷兰人执意用纺织品、布料和鲱鱼支付货款结果他们失去了在俄国的优先地位
(18)
评论界早有人指出这种以外贸盈余来证明经济发达的“贸易主义”
(4)
是弗兰克此书的一大硬伤。所以说是硬伤,盖因其不是个资料多少的问题,而是个不合逻辑的问题。众所周知,我国现在就是世界外贸顺差最大的国家之一(仅次于日本),而美国则是世界头号外贸逆差国。这能说明我国如今是“世界经济中心”而美国则是比非洲还要惨的最“边缘”之地么?
有趣的是,当时的一般趋势是中国经济越繁荣,通货输出越明显,而在经济衰败时期便会出现通货回流。如宋金对峙时代南宋钱币长期北流入金,但到南宋末的最后数十年间,却出现了钱币回流现象
(14)
唐宋时期中国的贸易逆差就更为明显,这个时期中国贵金属的极度稀缺据说就与此有关。贵金属之外,当时中国一般通货的大量外流更蔚为大观,从“开元通宝”到宋代制钱,都曾广行于周边地区,几成“国际通货”,有似今日美国以美元支付逆差的结果
破财换虚荣的“贡赐”的明朝为高。可见在
农业时代的外贸需求一般主要是奢侈品需求,强大帝国的这种需求(可以货币支付的需求)往往高于衰弱国家,因而容易形成更大的逆差。初级工业化开始后大宗产品供给与大宗原材料需求同步增加,但如果它是与没有投资需求的传统农业国进行贸易,则它的大宗原材料需求会比大宗产品供给更易实现,从而也造成大量逆差
清代初年也曾厉行海禁“片帆不准入口”。康熙中叶解除海禁后也只限四口广州、漳州、宁波、云台山通商比宋元时代的口岸少得多。而且仅仅70年后又关闭了三口只限广州一口通商实际上回到了半海禁状态。在广州一口又实行官府特许的行商垄断制度加上对越来越多的货物实行禁止外贸包括军需品、粮食、铁、丝绸、马匹、书籍等对国民接触外国人的严厉限制如1759年《防夷五事》条规所规定的对“自由贸易”排拒岂是今日所谓的关税壁垒或贸易保护主义所能比拟的
农业时代世界史中相对发达地区贸易是逆差、通货纯流出的现象是大量的,相反的事例反而较少。
在汉帝国黄金流向西域的同时,罗马帝国的黄金也在向东流
在历史上,关税壁垒是重商主义的一种实践,而没有这种壁垒可能意味着“后重商主义”自由贸易时代,也可能意味着“前重商主义”命令经济时代
明清政府的外贸政策就更为保守。明代曾长期实行“片板不许入海,寸货不许入蕃”的海禁政策,完全取缔民间外贸,以至于逼商为“寇”,造成了绵延不绝的“倭寇”问题
清代的关税税则紊乱,黑幕重重,贪污勒索盛行,若就国库所得而言,其税率确实不高,不仅低于英国,而且也低于两宋。但这与自由贸易全不相干,
只反映了当时以“农本”立围,以地丁钱粮为“正供”,当局视外贸为不正经,犹如偷鸡摸狗,国家财政岂能寄望于此
在中国古代政府对外贸最积极的时代是宋元而非明清。两宋政府尤其是幅员缩小而军费浩繁的南宋政府基于财政需要而鼓励市舶贸易市舶口岸多达20余个还曾以抓壮丁式的方式强籍商人出海
(20)
。然而这种贸易并不“自由”。市舶司类似海关对进口货“抽解”征税1040这一税率比清代关税要高许多。但关键问题并不在此宋政府对外贸的最致命的控制实际上是在“抽解”后。纳完关税的货物并不能直接进入市场要先由市舶司统购统销一大部分号曰“博买”主要的舶货如奢侈品、镔铁、药材等全部收购其他货物也要由官府“博买”一半。“博买”后由官方编纲解运京师。“官市之余听市于民”
(21)
,真正能“自由贸易”的不过这些漏网之鱼而已。
然而弗兰克在这方面汲取的东西很少,他的基本立论几乎完全建立在外贸顺差这一点上,此书中译本取名《白银资本》(据说这个书名征求了弗兰克本人的意见)即因此而来
现实社会主义衰败了,自由主义也面临很多问题,究竟人类的道路在哪里,这只能从普世的角度,而不是什么西方或东方的角度来研究。
记得前几年《东方》杂志上有句话:谁是“东方”?小心地球是圆的,向东,向东,没准儿又回到了原地
传统帝国在对外商有时的确“真是太宽容了”的同时,对本国商民却极尽歧视、镇压乃至剿灭之能事,其手段一点也不“和平”。相应地作为“博弈”的另一方,当时的中国海商也常常以海盗的形式对祖国处在战争状态,从明代作为所谓“倭寇”主体的中国民间海商武装,到明清两代的林风、林道乾、刘香、送芝龙郑成功父子以至蔡牵、郭惟太等莫不如此,这其间哪有什么“用和平方式解决冲突”的文明规则
而在中文版前言中他又认为中国自古以来一直先进,直到鸦片战争后才衰落,“而这显然是暂时的”,现在它又将“再次占据世界经济的支配地位”
> 中国一直先进的观点,原来是他炮制出来的。
文化”不可比,但“制度”有优劣
他在努力使他的理论与当时在苏联居于正统地位的马克思主义周期危机的理论相适应,但他很快就在农业集体化中被当成异端而被捕并死于大肃反,这一努力遂告中断。
以对政府财政资助的依赖程度来衡量第三部门自主性受到的束缚,这样的论证在公民社会是可以成立的
(25)
,但放到其他社会类型就不行了
在前重商主义体制下,官方不仅限制进口,尤其禁阻出口(这与重商主义只限制进口但支持出口正相反),限制的目的也不是保护国内产业,而是便于管制国民
何况“康德拉季耶夫周期”在经济学上本有其明确的所指它是一种平均约为54年的兴衰周期。虽然历史学著述借用经济学概念时出现泛化是可以理解的但像弗兰克那样把从几千年到几十年的过程都算做“康德拉季耶夫周期”也未免过分了。依了这种逻辑恐龙的兴衰乃至天体的生灭是否都可以叫做“康德拉季耶夫周期”而且可以以这个概念作为原因来解释过程本身苟如此人们还有什么“谜”是不可解的呢
> 对所有的事情都可以用道家的相转化的理论来解释的
显然,尽管由于某种原因左右派都称道“东方”,“东方”好像既是社会主义的希望所在,又是自由主义或保守主义的希望所在,但我们没有理由去忽视“主义”本身所给出的真问题,而沉迷于所谓“东方”还是“西方”这样的假问题
,并以那种所谓萨克斯和崔之元都是“东方中心论者”的昏话把人搞糊涂。
于是在经济转轨问题上,左派、右派似乎都成了“东方中心论”者,都在以“中国的经验”教训欧洲人应当如何干。萨克斯教训欧洲人应当学习中国禁止民间工会,崔之元教训欧洲人应当学习中国搞“鞍钢宪法”——人们能把他们在“东方中心论”的名义下一锅煮么?
1939年由美国经济学家J·A·熊彼特提议后世界经济学界都接受了“康德拉季耶夫周期”这一术语作为经济成长中长时段波动的称呼。
### 谁,面向哪个东方?——评弗兰克:《重新面向东方》,兼论所谓“西方中心论”问题 / 一、外贸顺差与“经济中心
同样按这个逻辑,明清之际有大量白银流入中国,因此它是世界第一。那么我们怎么评价秦汉唐宋?那可是大量通货流出中国的时代——是大量外国商品传入中国的时代,
是“贸易”大量逆差的时代。如果用这种尺度评价,那两千多年中华帝国历史的大部分便成了大衰落的时代,一无可取的时代,龟缩于“世界体系之边缘”的时代,只有到了明清间的这几百年,才昙花一现,忽然崛起为“全球经济中心”,尔后又莫名其妙地忽然衰落
### 公社之谜——农业集体化的再认识
中国的大一统始于秦而关于奠定了强秦之基的商鞅变法过去史学界有个流行的论点即认为商鞅坏井田、开阡陌而推行了“土地私有制”。如今史学界仍持此论者恐已不多因为20世纪70年代以来人们从睡虎地出土秦简与青川出土的秦牍中已明确知道秦朝实行的是严格的国家授地制而不是什么“土地自由买卖”
1919年以后虽然村苏维埃普遍设立但传统村社的势力仍然强大形成所谓乡村中“两个政权并存”的局面
(36)
。在20世纪20年代由于大多数村苏维埃没有预算而米尔村社则控制着土地和社区公共资源因此往往比村苏维埃更具实质功能。经过革命“警察式”管理衰落而“公社式”管理更活跃村社的自治性因而也增加了。当时村苏维埃的选举要讲“阶级原则”“富农”没有选举和被选举权而村社及村会的选举则是传统式的不讲什么“阶级”于是所谓“富农”控制村社便成了布尔什维克体制在农村遇到的一大问题。
不久中央便正式决定在全国取消农会。紧接着在这年10月间集体化最重要的逻辑前提——统购统销即正式出台。取消农会的决定在主观上虽未必与统购统销这一重大转折有关但此举无疑使国家在这一转折关头消除了一个潜在的谈判对手面对着一盘散沙式的小农户其地位远比面对着自治村社的苏俄国家要有利。至此我国农村组织前所未有的一元化任何可能制衡大共同体的自治机制都不存在
集体化在中国遇到的阻力就小得多。中国农民抵制集体化的高潮发生在1956年的高级社时期
在某种意义上,传统俄国社会类似于传统(中世纪)西欧与传统中国之间的中介类型。
与西欧贵族相比,俄国贵族具有浓厚的官僚气味;而与传统中国官僚相比,则又具有浓厚贵族色彩。同样的,传统俄国乡村组织——米尔公社与中古西欧的小共同体相比具有明显的“政社合一”式的官办色彩,但与传统中国的乡里保甲相比却显得更像个自治的小共同体。传统西欧是“小共同体本位”社会,个人依附于采邑、村社、教区、行会乃至家族等传统小共同体,个性发展受其抑制。但
浙江是全国农潮最严重的省份宁波专区有5社员退社想退社而未遂的达20为全国之冠
1956年农潮之后到1958年公社化时农民就再未发生反抗之风甚至在大饥荒导致上千万人饿死时亦然
当年苏联为了迫使农民——土地公有的传统村社社员接受集体化,曾付出了惨烈的代价:逮捕、流放了上百万“富农”;出动成师的正规红军和飞机大炮镇压农民反抗;在一些地区的镇压,其惨烈程度甚至导致某些红军部队(他们也是“穿军装的农民”)的哗变
回头再看中国,对于“小私有”的中国农民更容易被“集体化”便不会觉得奇怪。如前所述,在中国农民集体化的全过程中相对较强的抵制发生在东南沿海的广东、浙江、江苏诸省,而这三省(尤其是前二省)在近代恰是中国民间传统小共同体——宗族组织最活跃的地方
### 土地改革=民主革命?集体化=社会主义?——马克思主义农民理论的演变与发展 / 一、“两种保守性”与小生产衰亡论
如上所述,马克思、恩格斯认为农民“小生产”及作为这种生产基础的“小土地私有”相对于“大生产”(无论是资本主义还是社会主义的)而言是“保守”的、乃至“反动”的。但过去我们也知道,马克思、恩格斯对小私有农民在反封建问题上的作用有另外的评价。马克思在赞赏法国大革命时曾指出,资产阶级革命的基础“就是消灭农村中的封建制度,就是创立自由的占有土地的农民阶级”
(17)
。他还提到:“自耕农的这种自由小土地所有制形式,作为占统治地位的正常形式……是封建土地所有制解体所产生的各种形式之一。……在这里,土地的所有权是个人独立发展的基础。它也是农业本身发展的一个必要的过渡阶段。”
(18
### 土地改革=民主革命?集体化=社会主义?——马克思主义农民理论的演变与发展
只有到了“市民社会”个人依靠“交换的力量”冲破了共同体的束缚,结束“人的依附性”而形成“人的独立性”。
进而克服马克思认为是因私有财产而带来的“异化”,走向“自由个性”和“自由人联合体”的理想状态。
问题的实质在于:作为近代经济组织的合作制,是商品生产者的自由联合体,是契约性社会中商品生产者为市场竞争中的共同利益而在产、供、销等各领域或信贷、科技、机械服务等方面形成的联营组织。
它的前提便是要有商品生产者自由个性的觉醒、经济理性的成熟,作为契约主体的独立人格(包括法人人格)的存在以及社会交换关系的发达。而身份性社会中的传统农村公社和命令经济(习俗经济)中的“集体”却是一种以人身依附为基础的共同体,一种以压抑人的个性自觉与否定契约人格为条件的束缚一保护纽带。两者所体现的社会性质与经济关系是全然不同甚至对立的

View File

@ -8,6 +8,8 @@ exportbooknotes.py
- 命令行菜单按最近打开时间降序展示书籍列表供用户选择导出 - 命令行菜单按最近打开时间降序展示书籍列表供用户选择导出
- 仅导出选中书籍的所有笔记按章节分组生成Markdown文件 - 仅导出选中书籍的所有笔记按章节分组生成Markdown文件
依赖config.py 统一管理路径和配置项
主要数据流 主要数据流
1. 数据同步到data目录 1. 数据同步到data目录
2. 解析Books.plist获取书籍元数据 2. 解析Books.plist获取书籍元数据
@ -19,10 +21,13 @@ exportbooknotes.py
依赖Python 3, InquirerPy, bs4, shutil, os, datetime, sqlite3 依赖Python 3, InquirerPy, bs4, shutil, os, datetime, sqlite3
主要数据流
典型用法 典型用法
python exportbooknotes.py python exportbooknotes.py
# 按提示选择书籍自动导出笔记到export_notes目录 # 按提示选择书籍自动导出笔记到export_notes目录
""" """
import config
""" """
自动生成 booksnote 数据结构 自动生成 booksnote 数据结构
booksnote = { booksnote = {
@ -63,7 +68,7 @@ def get_toc_tree(toc_path):
#pprint(toc_tree, indent=2, depth=5) #pprint(toc_tree, indent=2, depth=5)
return toc_tree return toc_tree
def build_booksnote(annotation_db='data/AEAnnotation.sqlite', books_plist='data/Books.plist', bookid=None): def build_booksnote(annotation_db=config.LOCAL_ANNOTATION_DB, books_plist=config.LOCAL_BOOKS_PLIST, bookid=None):
# 支持只处理特定 assetid 的笔记 # 支持只处理特定 assetid 的笔记
annotations = get_annotations(annotation_db, bookid=bookid) annotations = get_annotations(annotation_db, bookid=bookid)
booksinfo = parse_books_plist(books_plist) booksinfo = parse_books_plist(books_plist)
@ -148,11 +153,11 @@ if __name__ == '__main__':
import os.path import os.path
# 自动覆盖 ./data 下的数据库和plist文件源为iBooks真实路径 # 自动覆盖 ./data 下的数据库和plist文件源为iBooks真实路径
src_files = [ src_files = [
(os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite'), 'data/AEAnnotation.sqlite'), (config.IBOOKS_ANNOTATION_DB, config.LOCAL_ANNOTATION_DB),
(os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite-shm'), 'data/AEAnnotation.sqlite-shm'), (config.IBOOKS_ANNOTATION_SHM, config.LOCAL_ANNOTATION_SHM),
(os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/AEAnnotation/AEAnnotation_v10312011_1727_local.sqlite-wal'), 'data/AEAnnotation.sqlite-wal'), (config.IBOOKS_ANNOTATION_WAL, config.LOCAL_ANNOTATION_WAL),
(os.path.expanduser('~/Library/Containers/com.apple.iBooksX/Data/Documents/BKLibrary/BKLibrary-1-091020131601.sqlite'), 'data/BKLibrary.sqlite'), (config.IBOOKS_LIBRARY_DB, config.LOCAL_LIBRARY_DB),
(os.path.expanduser('~/Library/Containers/com.apple.BKAgentService/Data/Documents/iBooks/Books/Books.plist'), 'data/Books.plist') (config.IBOOKS_BOOKS_PLIST, config.LOCAL_BOOKS_PLIST)
] ]
for src, dst in src_files: for src, dst in src_files:
if os.path.exists(src): if os.path.exists(src):
@ -165,7 +170,7 @@ if __name__ == '__main__':
from InquirerPy import inquirer # type: ignore from InquirerPy import inquirer # type: ignore
# 先获取所有书籍元数据 # 先获取所有书籍元数据
booksinfo = parse_books_plist('data/Books.plist') booksinfo = parse_books_plist(config.LOCAL_BOOKS_PLIST)
# 构建书名列表优先displayname, 其次itemname, 否则assetid按parse_books_plist中的date字段排序 # 构建书名列表优先displayname, 其次itemname, 否则assetid按parse_books_plist中的date字段排序
assetid2name = {} assetid2name = {}
@ -173,7 +178,7 @@ if __name__ == '__main__':
from booklist_parse import get_books_last_open from booklist_parse import get_books_last_open
# 获取所有书籍的最后打开时间(字典,值为{'last_open': 时间戳} # 获取所有书籍的最后打开时间(字典,值为{'last_open': 时间戳}
last_open_times = get_books_last_open('data/BKLibrary.sqlite') last_open_times = get_books_last_open(config.LOCAL_LIBRARY_DB)
for assetid, info in booksinfo.items(): for assetid, info in booksinfo.items():
name = info.get('displayname') or info.get('itemname') or assetid name = info.get('displayname') or info.get('itemname') or assetid

BIN
icons/.DS_Store vendored Normal file

Binary file not shown.

BIN
icons/Cbb20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/Drop Stuff.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

BIN
icons/Pixadex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
icons/Safari.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/add.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/addbook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

BIN
icons/amazon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
icons/book.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/book3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
icons/book_open.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 631 B

BIN
icons/books.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
icons/booksicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
icons/conf.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
icons/config.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icons/direction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icons/down.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
icons/downr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/dustbin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/emblem_library.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/fav.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
icons/file18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/flower.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
icons/flush.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

BIN
icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
icons/foldup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
icons/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
icons/homepage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
icons/import.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 616 B

BIN
icons/kindle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 B

BIN
icons/list.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
icons/mail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
icons/md.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

BIN
icons/md2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
icons/money.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

BIN
icons/money2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
icons/money3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icons/person.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
icons/question.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

BIN
icons/refresh.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

BIN
icons/refresh2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/register.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

BIN
icons/search.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
icons/share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
icons/statistics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
icons/txt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
icons/upgreen.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
icons/user.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
icons/web.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
icons/webchat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
icons/write.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
icons/yes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

BIN
kmanapp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

58
kmanapp.qrc Normal file
View File

@ -0,0 +1,58 @@
<RCC>
<qresource prefix="/">
<file>icons/add.png</file>
<file>icons/books.png</file>
<file>icons/Cbb20.png</file>
<file>icons/config.png</file>
<file>icons/direction.png</file>
<file>icons/down.png</file>
<file>icons/downr.png</file>
<file>icons/dustbin.png</file>
<file>icons/fav.png</file>
<file>icons/file18.png</file>
<file>icons/folder.png</file>
<file>icons/home.png</file>
<file>icons/list.png</file>
<file>icons/mail.png</file>
<file>icons/md2.png</file>
<file>icons/money.png</file>
<file>icons/money2.png</file>
<file>icons/money3.png</file>
<file>icons/qq.png</file>
<file>icons/refresh2.png</file>
<file>icons/Safari.png</file>
<file>icons/search.jpeg</file>
<file>icons/share.png</file>
<file>icons/txt.png</file>
<file>icons/upgreen.png</file>
<file>icons/web.png</file>
<file>icons/webchat.png</file>
<file>icons/write.png</file>
<file>icons/yes.png</file>
<file>icons/addbook.png</file>
<file>icons/conf.png</file>
<file>icons/Drop Stuff.png</file>
<file>icons/flush.png</file>
<file>icons/foldup.png</file>
<file>icons/md.jpeg</file>
<file>icons/Pixadex.png</file>
<file>icons/question.png</file>
<file>icons/refresh.png</file>
<file>icons/statistics.png</file>
<file>mainwindow.ui</file>
<file>icons/booksicon.png</file>
<file>icons/book_open.png</file>
<file>icons/book_open_bookmark.png</file>
<file>icons/emblem_library.png</file>
<file>icons/book3.png</file>
<file>icons/register.png</file>
<file>icons/person.png</file>
<file>icons/user.png</file>
<file>icons/kindle.png</file>
<file>icons/homepage.png</file>
<file>icons/import.png</file>
<file>icons/book.png</file>
<file>icons/amazon.png</file>
<file>icons/flower.png</file>
</qresource>
</RCC>

257
mainwindow.ui Normal file
View File

@ -0,0 +1,257 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>837</width>
<height>622</height>
</rect>
</property>
<property name="windowTitle">
<string>Kindle Management</string>
</property>
<property name="windowIcon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/Cbb20.png</normaloff>:/icons/Cbb20.png</iconset>
</property>
<property name="iconSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QSplitter" name="splitter_2">
<property name="orientation">
<enum>Qt::Orientation::Horizontal</enum>
</property>
<widget class="QTreeView" name="treeView">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>401</width>
<height>16777215</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Orientation::Vertical</enum>
</property>
<widget class="QTableView" name="tableView"/>
<widget class="QTextBrowser" name="textEdit"/>
</widget>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="searchLabel">
<property name="text">
<string>Search</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchLineEdit">
<property name="placeholderText">
<string>可按书名、作者、内容搜索笔记</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="searchComboBox">
<property name="currentText">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="searchToolButton">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/search.jpeg</normaloff>:/icons/search.jpeg</iconset>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QMenuBar" name="menuBar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>837</width>
<height>32</height>
</rect>
</property>
</widget>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actionimportkindle"/>
<addaction name="actionimportlocal"/>
<addaction name="separator"/>
<addaction name="actionexport"/>
<addaction name="actionwords"/>
<addaction name="actionstatistic"/>
<addaction name="separator"/>
<addaction name="actionconfig"/>
<addaction name="separator"/>
<addaction name="actionhomepage"/>
<addaction name="actionabout"/>
<addaction name="separator"/>
<addaction name="actionflush"/>
</widget>
<action name="actionimportlocal">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/downr.png</normaloff>:/icons/downr.png</iconset>
</property>
<property name="text">
<string>importlocal</string>
</property>
<property name="toolTip">
<string>import clipping file from local clipping file</string>
</property>
</action>
<action name="actionimportkindle">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/kindle.png</normaloff>:/icons/kindle.png</iconset>
</property>
<property name="text">
<string>importkindle</string>
</property>
<property name="toolTip">
<string>import clipping file from kindle</string>
</property>
</action>
<action name="actionconfig">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/config.png</normaloff>:/icons/config.png</iconset>
</property>
<property name="text">
<string>config</string>
</property>
<property name="toolTip">
<string>configuration</string>
</property>
</action>
<action name="actionflush">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/refresh.png</normaloff>:/icons/refresh.png</iconset>
</property>
<property name="text">
<string>refresh</string>
</property>
<property name="toolTip">
<string>refresh import file/quick import from kindle</string>
</property>
</action>
<action name="actionwords">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/books.png</normaloff>:/icons/books.png</iconset>
</property>
<property name="text">
<string>words</string>
</property>
<property name="toolTip">
<string>words</string>
</property>
</action>
<action name="actionstatistic">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/statistics.png</normaloff>:/icons/statistics.png</iconset>
</property>
<property name="text">
<string>statistic</string>
</property>
<property name="toolTip">
<string>statistics reading habbit</string>
</property>
</action>
<action name="actionhomepage">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/web.png</normaloff>:/icons/web.png</iconset>
</property>
<property name="text">
<string>homepage</string>
</property>
<property name="toolTip">
<string>redirect to my homepage</string>
</property>
</action>
<action name="actionabout">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/question.png</normaloff>:/icons/question.png</iconset>
</property>
<property name="text">
<string>about</string>
</property>
<property name="toolTip">
<string>open about dialog</string>
</property>
</action>
<action name="actionsearch">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/Pixadex.png</normaloff>:/icons/Pixadex.png</iconset>
</property>
<property name="text">
<string>search</string>
</property>
<property name="toolTip">
<string>search note</string>
</property>
</action>
<action name="actionexport">
<property name="icon">
<iconset resource="kmanapp.qrc">
<normaloff>:/icons/md2.png</normaloff>:/icons/md2.png</iconset>
</property>
<property name="text">
<string>export</string>
</property>
<property name="toolTip">
<string>export to file</string>
</property>
</action>
</widget>
<resources>
<include location="kmanapp.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,3 +1,11 @@
#!/bin/bash
# push.sh
# -----------
# 用途一键提交并推送当前目录下所有git更改。
# 用法:
# ./push.sh "commit message"
# # 若不传参数默认commit message为'update'
#!/bin/bash #!/bin/bash
git add . git add .
msg="${1:-'update'}" msg="${1:-'update'}"

123
test.ui Normal file
View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>32</height>
</rect>
</property>
<widget class="QMenu" name="menutest1">
<property name="title">
<string>test1</string>
</property>
</widget>
<widget class="QMenu" name="menutest2">
<property name="title">
<string>test2</string>
</property>
</widget>
<widget class="QMenu" name="menutest3">
<property name="title">
<string>test3</string>
</property>
</widget>
<addaction name="menutest1"/>
<addaction name="menutest2"/>
<addaction name="menutest3"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
<property name="windowTitle">
<string>toolBar</string>
</property>
<attribute name="toolBarArea">
<enum>TopToolBarArea</enum>
</attribute>
<attribute name="toolBarBreak">
<bool>false</bool>
</attribute>
<addaction name="actiontest1"/>
<addaction name="separator"/>
<addaction name="actiontest2"/>
<addaction name="actiontest3"/>
<addaction name="actiontest5"/>
<addaction name="actiontestre4"/>
</widget>
<action name="actiontest1">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentSave"/>
</property>
<property name="text">
<string>test1</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actiontest2">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::DocumentSend"/>
</property>
<property name="text">
<string>test2</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actiontest3">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::FolderNew"/>
</property>
<property name="text">
<string>test3</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actiontestre4">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditPaste"/>
</property>
<property name="text">
<string>testre4</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
<action name="actiontest5">
<property name="icon">
<iconset theme="QIcon::ThemeIcon::EditClear"/>
</property>
<property name="text">
<string>test5</string>
</property>
<property name="menuRole">
<enum>QAction::MenuRole::NoRole</enum>
</property>
</action>
</widget>
<resources>
<include location="kmanapp.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -1,9 +1,21 @@
# parsetoc.py """
# ----------------------------- toc_parse.py
# 用于解析EPUB电子书的toc.ncx目录文件递归构建章节树结构支持通过ref和filepos查找完整label路径。 ------------
# 支持多种EPUB格式的toc.ncx包含批量测试用例。 功能
# 依赖BeautifulSoup4 - 解析EPUB电子书的toc.ncx目录文件递归构建章节树结构
# ----------------------------- - 支持通过ref和filepos查找完整label路径
- 支持通过selectedtext在html文件中定位章节标题
- 兼容多种EPUB格式支持批量测试
依赖config.py 统一管理路径和配置项
主要接口
parse_navpoints(navpoints) # 递归解析navPoint节点返回章节树结构。
find_label_path(node, ref, filepos, path) # 查找指定ref和filepos的章节label路径。
find_section_by_selectedtext(html_path, selectedtext) # 通过选中文本定位章节标题。
parse_html_title(html_path) # 解析html文件标题。
依赖BeautifulSoup4, pprint, os, typing
"""
import config
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
@ -160,14 +172,14 @@ def find_label_path(
if __name__ == "__main__": if __name__ == "__main__":
# ==== 批量测试指定toc/html/filepos列表 ==== # ==== 批量测试指定toc/html/filepos列表 ====
test_cases = [ test_cases = [
["examples/epub_format_1", "index_split_015.html", "filepos684970"], [config.EXAMPLES_DIR + "/epub_format_1", "index_split_015.html", "filepos684970"],
["examples/epub_format_2", "Text/8c7276f38ead4738ee19249418898c18_split_006.html", "sigil_toc_id_12"], [config.EXAMPLES_DIR + "/epub_format_2", "Text/8c7276f38ead4738ee19249418898c18_split_006.html", "sigil_toc_id_12"],
["examples/epub_format_3", "Text/011.xhtml", ""], [config.EXAMPLES_DIR + "/epub_format_3", "Text/011.xhtml", ""],
["examples/epub_format_4", "xhtml/p-006.xhtml", ""], [config.EXAMPLES_DIR + "/epub_format_4", "xhtml/p-006.xhtml", ""],
["examples/变宋", "text/part0005.html", ""], [config.EXAMPLES_DIR + "/变宋", "text/part0005.html", ""],
["examples/变宋", "text/part0002_split_003.html", ""], [config.EXAMPLES_DIR + "/变宋", "text/part0002_split_003.html", ""],
["examples/规训与惩罚", "index_split_006.html", ""], [config.EXAMPLES_DIR + "/规训与惩罚", "index_split_006.html", ""],
["examples/政治哲學的12堂Podcast", "ch1.xhtml#_idParaDest-4", ""], [config.EXAMPLES_DIR + "/政治哲學的12堂Podcast", "ch1.xhtml#_idParaDest-4", ""],
] ]
for epub_dir, html_file, filepos in test_cases: for epub_dir, html_file, filepos in test_cases:
# 自动查找epub目录下的toc.ncx # 自动查找epub目录下的toc.ncx
@ -212,7 +224,7 @@ if __name__ == "__main__":
note_idref = 'text/part0002_split_003.html' note_idref = 'text/part0002_split_003.html'
note_filepos = None note_filepos = None
# 变宋toc.ncx路径 # 变宋toc.ncx路径
bian_song_toc = "examples/变宋/toc.ncx" bian_song_toc = config.EXAMPLES_DIR + "/变宋/toc.ncx"
import os import os
if os.path.exists(bian_song_toc): if os.path.exists(bian_song_toc):
with open(bian_song_toc, "r", encoding="utf-8") as f: with open(bian_song_toc, "r", encoding="utf-8") as f:

11158
x

File diff suppressed because one or more lines are too long