kindle manager

This commit is contained in:
douboer
2020-06-30 12:06:47 +08:00
36 changed files with 22841 additions and 27699 deletions

6
CLIP.md Normal file
View File

@@ -0,0 +1,6 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|--
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。--CG注:山寨 假货 问题
HL|庆余年(精校版)|猫腻|2020/1/19 20:00:29|园子里的护卫能掺多少人就掺多少人,我会派人盯着
HL|庆余年(精校版)|猫腻|2020/1/20 19:57:10|叶灵儿叹了口

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +1,80 @@
# Change Log # Change Log
## 1.0.0 (20200526) ## 1.1.1 (20200626)
### create ### feature
- unittest - remove pandas library because it will make the package too large,
- abstract note/bookmark/highlight from kindle clipping I write mTable calss to operate table data structure
- formatter output to special file - add sort function in nTableModel class which
make table can be sort by clicking table header!! cheer!!
- make installer package by pyistaller, but run "open kmanapp.app",<br>
always show:<br>
**LSOpenURLsWithRole() failed with error -10810 for the file /Users/mark/penv/kman/Release/kmanapp.app.**<br>
which spend me a great mount of time,<br>
and I baidu/bing/google all about discuss try to solve it,<br>
but problem still here! :cry: :cry: :cry:<br>
- modify some bugs let application more stabler
### learn lesson
- how to write a iterable object
## 1.1.0 (20200617)
### feature
- book information grabbed from douban / amazon
## 1.0.5 (20200613)
### feature
- backup clips after kman closed, check and read backup file when kman started
- import words from kindle, export filtered words to MD/CSV
### learn lesson
- create virtual envirement
```
> mkvirtualenv kmanenv
> workon kmanenv
virtual env lib in ~/.virtualenvs/kmanenv/
```
## 1.0.4 (20200604)
### feature
- refactoring kman.py
- creat constant class for all constant
- creat config dialog to do some constant configuration
- creat xman class for all clipping manipulation
- design config dialog & statusbar information
### learn lesson
- generate ui source(.py) use pyqtuic will 'import kmanapp\_rc',
so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o kmanapp\_rc.py
- use QTime to check kindle kindle is connected or not
- implement table model which inherited from QAbstractTableModel, so
we can use **panda data structure**
## 1.0.3 (20200603)
### feature
- design GUI with qdesigner
- define toolbar action's slots
## 1.0.2 (20200530)
### feature
- remove duplication
- refactor some code
- optimize search feature
## learn lesson
- change dict keys between iteration, will throw RuntimeError:
dictionary changed size during iteration
- reference - http://www.cocoachina.com/articles/89748
- pyQt5 reference - https://www.learnpyqt.com/
## 1.0.1 (20200528) ## 1.0.1 (20200528)
### feature ### feature
- add search clip and some functions - add search clip and some functions
## learn lesson ### learn lesson
- assign value to a not exist key, will throw KeyError, too inflexible!!! perl much better - assign value to a not exist key, will throw KeyError, too inflexible!!! perl much better
- use defaultdict to solve obove problem, note, defaultdict only create two layer key auto - use defaultdict to solve obove problem, note, defaultdict only create two layer key auto
``` ```
@@ -40,64 +103,24 @@ b['1']['2'] = {'3':1} # OK
- unittest error line number is not collect, **unitest bug**? - unittest error line number is not collect, **unitest bug**?
- **unitest testcase execution sequence is disorder, so must create test data for each case** - **unitest testcase execution sequence is disorder, so must create test data for each case**
## 1.0.2 (20200530)
### feature
- remove duplication
- refactor some code
- optimize search feature
## learn lesson ## 1.0.0 (20200526)
- change dict keys between iteration, will throw RuntimeError: ### create
dictionary changed size during iteration - unittest
- reference - http://www.cocoachina.com/articles/89748 - abstract note/bookmark/highlight from kindle clipping
- pyQt5 reference - https://www.learnpyqt.com/ - formatter output to special file
## 1.0.3 (20200603)
### feature
- design GUI with qdesigner
- define toolbar action's slots
## 1.0.4 (20200604)
### feature
- refactoring kman.py
- creat constant class for all constant
- creat config dialog to do some constant configuration
- creat xman class for all clipping manipulation
- design config dialog & statusbar information
## learn lesson
- generate ui source(.py) use pyqtuic will 'import kmanapp\_rc',
so I need to use resouce file(.qrc) to manage resouce(icons),
and generator rcc binnay file, must kmanapp\_rc.py
command: pyside2-rcc -binary kmanapp.qrc -o kmanapp\_rc.py
- use QTime to check kindle kindle is connected or not
- implement table model which inherited from QAbstractTableModel, so
we can use **panda data structure**
## 1.0.5 (20200613)
### feature
- backup clips after kman closed, check and read backup file when kman started
- import words from kindle, export filtered words to MD/CSV
## learn lesson
## 1.1.0 (20200617)
### feature
- book information grabbed from douban / amazon
--- ---
# feature list # feature list (keep update / 20200625)
- [x] first abstract from kindle hard / local directory for different OS - [x] first abstract from kindle hard / local directory for different OS
- [x] add GUI use QT - [x] add GUI use QT
- [x] use thread to check kindle connection status - [x] use thread to check kindle connection status
- import function: - import function:
- [x] local - [x] local
- [x] kindle - [x] kindle
- [ ] duokan - [x] duokan
- [ ] amazon - [x] amazon **not test**
- [x] link to douban and amazon - [x] link to douban and amazon
- export function: - export function:
- [ ] to [evernote](https://github.com/benhorvath/kindle2evernote/blob/master/kindle2evernote.py) - [ ] to [evernote](https://github.com/benhorvath/kindle2evernote/blob/master/kindle2evernote.py)
@@ -106,7 +129,6 @@ b['1']['2'] = {'3':1} # OK
- [ ] onenote - [ ] onenote
- [x] txt - [x] txt
- [x] markdown - [x] markdown
- [ ] easily copy filter notes to clipboard
- [ ] implement command line based on [argparse](https://www.jianshu.com/p/a41fbd4919f8) - [ ] implement command line based on [argparse](https://www.jianshu.com/p/a41fbd4919f8)
- :x: mobi / epub reader - :x: mobi / epub reader
- [ ] import .azw3/.epub/.mobi file to kindle - [ ] import .azw3/.epub/.mobi file to kindle
@@ -121,5 +143,10 @@ b['1']['2'] = {'3':1} # OK
- [ ] clean (sdr) - [ ] clean (sdr)
- [ ] sync modify to kindle device - [ ] sync modify to kindle device
- [ ] compare parse html info with xpath & beautisoap & regex - [ ] compare parse html info with xpath & beautisoap & regex
- [x] save export markdown file name with book name(treeview's item name),<br>
such as **<<印度漂浮的次大陆.md>>**
- [x] easily copy filter notes to clipboard
- [x] add right click menu for book info treeview item, so I can copy book info by select menu, format:<br>
1| :book: | [存在主义咖啡馆](href) | 作者/出版社/出版时间/阅读时间 书评

2
cui
View File

@@ -1,2 +1,4 @@
pyuic mainwindow.ui -o mainwindow.py --no-protection pyuic mainwindow.ui -o mainwindow.py --no-protection
pyside2-rcc -binary kmanapp.qrc -o kmanapp_rc.py pyside2-rcc -binary kmanapp.qrc -o kmanapp_rc.py
cp -fr *py *md *ico *qrc *ui ~/penv/kman/

20133
debug Normal file

File diff suppressed because it is too large Load Diff

BIN
downimg/s29675043.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

179
export.md
View File

@@ -1,122 +1,61 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|-- --|--|--|--|--
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:30:12|关于病菌的历史作用的最令人生畏的例子来自随同哥伦布1492年的航行而开始的欧洲人对美洲的征服。虽然被那些杀人不眨眼的西班牙征服者杀死的印第安人不计其数但凶恶的西班牙病菌杀死的印第安人却要多得多。为什么在欧洲和美洲之间这种可怕的病菌的交流是这样不对等为什么印第安人的疾病没有大批杀死西班牙入侵者并传回欧洲消灭掉欧洲95%的人口?
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:12:49|这就是边际效用递减的规律,它指的是每多消耗一个单位的商品,所能带来的新增的享受在递减。例如我们吃东西的时候,食物带给我们的边际效用,通常都是递减的 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:32:56|可以从数学上把病菌的传播定义为:由每一个原发病人传染的新的受害者的数目。这个数目的大小取决于每一个受害者能够传染给新的受害者的持续时间的长短,以及这种病菌从一个受害者转移到下一个受害者的效率的高低
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 12:33:44|的是,在新闻里,我们不仅会听到抱怨贸易逆差,还会听到抱怨贸易顺差。当人们抱怨贸易逆差时,说我们国家的钱都被外国人赚走了。这时,他们没有提到,由于进口了许多外国商品,他们国家的人享受了许多优质商品。当人们抱怨贸易顺差时,他们会说,我们国家的资源都被外国人给买走了。但这时他们没有提,我们赚了不少外国人的钱,从而获取了将来进一步享受外国人提供的商品和服务的机会 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:33:12|病菌以各种稀奇古怪的方式使我们生病,如使我们得生殖器溃疡或腹泻。它这样做会得到什么样的演化利益呢?这似乎是特别令人费解而又自拆台脚的事,因为病菌杀死了它的宿主也就杀死了它自己
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:07|要成功实施价格歧视,最重要的就是要防止消费者之间对同样的产品进行转售。越是难以阻止消费者转售的产品,就越难实施价格歧视 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:33:47|我们的疾病的许多“症状”,实际上不过是某种非常聪明的病菌在改变我们的身体或行为以便使我们帮助传播病菌时所表现出来的一些方式而已
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:36|但对于那些不能转售的商品或者服务,商人就会进行大量价格歧视,比如在餐厅里喝的水和饮料,和餐厅以外的便利店比,它们的价格差距就非常大。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:43:11|这就是为什么“使我们生病”是符合病菌的利益的
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:42:49|人们对一种产品趋之若鹜的地区,价格就定得高一点; HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 16:43:17|但是,为什么病菌会演化出杀死宿主这种明显自拆台脚的策略呢?
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:43:35|价格歧视的三种方法 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:05:35|人类历史上最大的一次流行病是在第一次世界大战结束时杀死2100万人的流行性感冒。黑死病(腺鼠疫)在1346年到1352年间杀死了欧洲四分之一的人口在有些城市里死亡人数高达70%。19世纪80年代初当加拿大太平洋铁路修经萨斯喀彻温时该省以前很少接触过白人及其病菌的印第安人死于肺结核的人数每年竟达到惊人的9%。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:44:23|实名制的飞机票,也是大量实施价格歧视的典型案例 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:08:40|但由于这种病菌除了在活人体内是不可能生存的,所以人死了这种病也就消失了,直到又一批后代达到易受感染的年代——直到一个受到感染的外来人使一场流行病重新开始
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:47:00|如果演唱会门票价格过低,歌迷就不得不展开价格以外的竞争,那就是排队等候。什么人更愿意排队等候呢?是那些时间成本比较低的人,通常是年轻人。这样做的结果,是能够把那些更年轻、更热情、更奔放的歌迷吸引到演唱会现场,这样演唱会的现场就会更加火爆。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:38:20|在第一年,黏液病毒在受到感染的兔子中造成了令人满意的(对澳大利亚农民来说)998%的死亡率。令这些农民感到失望的是第二年兔子的死亡率下降到90%最后下降到25%,使得要在澳大利亚完全消灭兔子的希望落空了
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 9:47:06|还有一种非常有趣的价格歧视:歌星在发售演唱会门票时,有时会刻意把门票的价格降低。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:40:17|一提起梅毒我们立刻会联想到两种情况生殖器溃疡和病情发展的十分缓慢许多得不到治疗的患者要过好多年才会死去。然而当梅毒于1495年首次在欧洲明确见诸记录时它的脓疱通常从头部到膝部遍布全身使脸上的肉一块块脱落不消几个月就使人一命呜呼。到1546年梅毒已演化成具有我们今天所熟悉的那些症状的疾病。显然同多发性黏液瘤病一样为使患者活得长些而进行演化的那些梅毒螺旋体因此就能够把它们的螺旋体后代传染给更多的患者
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:22:51|弗里德曼强调的就是我们这里所说的优质的第二层含义。对消费者来说,并不是质量越高越好,也不是价格越低越好,而是价格和品质要相当,成本和收益要相当。这才是他们最看中的品质。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:42:57|1519年科尔特斯率领600个西班牙人在墨西哥海岸登陆去征服拥有好几百万人口的勇猛好战的阿兹特克帝国
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:14|好几年前,一辆校车发生意外,造成了严重的伤亡,社会各界人士出来说,应该制定校车的安全标准。我的一个朋友也参与了这一标准的制定,但后来他放弃了。因为他发现,如果不计成本凭空制定校车的安全标准,最后校车的标准快要接近坦克车的标准了 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:46:47|当我们美国人想到存在于1492年的新大陆人口最多的社会时出现在我们心头的往往只是阿兹特克人和印加人的那些社会
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:49:23|到1618年墨西哥原来2000万左右的人口急剧减少到160万左右
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:17|在我年轻的时候美国小学生所受到的教育是北美洲本来只有大约100万印第安人居住。把人数说得这样少对于为白人的征服行为辩解是有用的因为他们所征服的只不过是一个可以认为几乎是空无所有的大陆。然而考古发掘和对最早的欧洲探险者所留下的关于我们海岸地区的描写的仔细研究现已表明印第安人原来的人数在2000万左右。就整个新大陆来说据估计在哥伦布来到后的一两个世纪中印第安人口减少了95%。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:29:41|为了克服信息不对称,建立互信,人类社会构想出了各种各样有趣的解决方案,从重复交易到第三方背书,从质保、延保,再到收益共享。此外,还有三种非常接近的建立信任的办法:付出沉没成本、给出人质或者给出抵押。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:50|主要的杀手是旧大陆来的病菌。印第安人以前从来没有接触过这些病菌,因此对它们既没有免疫能力,也没有遗传抵抗能力。天花、麻疹、流行性感冒和斑疹伤寒争先恐后地要做杀手的头把交椅
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:32:11|银行是一个很难取信于人的行业。因为客户把钱交给银行万一哪天银行把钱卷走了客户就会血本无归。为了建立信任好银行往往把总部设在城市最繁华的地段而且不是租的是买的或者是自己盖的。这能让人相信它们打算一直在这里不会搬走它们要做50年、100年。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:50:58|好像这些病还嫌不够似的,紧随其后的还有白喉、疟疾、流行性腮腺炎、百日咳、瘟疫、肺结核和黄热病
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:33:44|越是愿意自残的人,越是容易取信于人,所以中国人才有所谓“感情深一口闷”之类酒桌上的谚语 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 17:54:00|梅毒、淋病、肺结核和流行性感冒于1779年随库克船长[5]到来接着于1804年又发生了一场斑疹伤寒大流行以及后来的许多“较小的”流行病把夏威夷的人口从1779年的50万左右减少到1853年的84000人
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:02|可以推测,当我们这个社会信任机制建立得越来越健全,人与人之间可以通过其他各种各样的方式来建立信任时,通过喝酒等自残的方式来建立信任的办法就会用得越来越少,而喝酒就变得纯粹是娱乐了 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 18:47:19|正如人类学家克劳德·莱维-斯特劳斯所说的那样,古代文字的主要功能是“方便对别人的奴役”。非专职人员个人使用文字只是很久以后的事,因为那时书写系统变得比较简单同时也更富于表现力
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:45|跟付出沉没成本相似的另外一个办法,是给出人质。一个大国的国王跟小国的国王说:“你放心吧,我不会侵略你,你们不要搞军备、不要武装起来了。”小国相信吗?小国不相信。但如果大国国王把自己的女儿嫁到小国去做人质,小国就比较容易相信了。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 20:59:22|文字只在新月沃地、墨西哥、可能还有中国独立出现,完全是因为这几个地方是粮食生产在它们各自的半球范围内出现的最早地区
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:35:51|当然,除了交出人质以外,付出抵押也是同样的办法。如果既没有人质,又没有抵押,那该怎么办?还有一些比较便宜的办法,就是互相分享一点秘密,互相交个底,说点自己以前见不得光的事情,那也算是一种无形的抵押 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:36:29|问题似乎在他们的社会。否则又怎样来解释澳大利亚东北部的土著为什么没有采用弓箭?而他们见过与他们进行贸易的托雷斯海峡的岛上居民在使用弓箭
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:38:28|厂商用实际行动告诉消费者我已经花掉了一个亿将来只有一个办法才能把这一个亿收回来那就是在未来若干年里持续地卖出10亿瓶水每瓶水都保证质量每瓶水都多收一毛钱。在出售这10亿瓶水的销售期内如果被发现质量问题消费者就会减少甚至停止购买厂商前面付出的那一个亿就打水漂了。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:38:01|1942年当第二次世界大战仍在进行时美国政府制定了曼哈顿计划其显而易见的目的就是抢在纳粹之前发明出为制造原子弹所需要的技术。3年后这个计划成功了共花去20亿美元相当于今天的200多亿美元
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:50:02|这时,有意思的结论就来了。从斯密和李嘉图开始,我们就知道分工能够提高生产效率,分工越细,人们就越能够集中生产自己擅长的东西,然后进行交换,这样人们的整体福利就能得以提高。 但是我们这里讲品质管理时是反其道而行的,我们要做的是垂直整合:加工厂不仅要有自己的奶牛,还要有自己的饲料来源。从种草、割草、送草,到养牛、挤奶、检验、加工,全是一家企业内部完成的工序。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:43:39|特利布·戴姆勒得以在一辆自行车上安装了一台发动机从而制造了第一辆摩托车
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:58:49|如果半成品或者成品的检验成本比较低,换手的次数就可以多些,就可以进行更细的分工;相反,如果品质检验的成本比较高,就需要更多地进行垂直整合,减少中间环节 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:45:48|从专利法律师观点看,最佳的发明就是全无先例的发明,就像雅典娜整个地从宙斯的前额跳出来一样[3]
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:00:09|损失掉一部分品牌的多样性,换来的是稳定性和可靠性,这么做可能是现代社会提高产品质量可靠性的一种趋势。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 21:50:58|从来就没有这样的人。所有公认的著名发明家都有一些有本领的前人和后人,而且他们是在社会有可能使用他们的成果的时候对原来的发明作出改进的
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:18:55|他的理由是,在这种产品发生意外对顾客造成伤害的案件中,要让顾客提出证据证明厂商存在疏忽是非常困难的,顾客必须是专家才有可能提出证据。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 23:58:58|在现代的西方,保护发明者的所有权的专利权和其他财产法奖励发明,而在现代的中国,缺乏这种保护妨碍了发明
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:19:26|他认为只要产品出现明显的质量问题,无需证据就可以判企业疏忽,企业应该对自己的产品负有严格的责任,顾客不需要提供关于疏忽的证据就能够获得赔偿。这种无条件的责任叫“严格责任” HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/28 23:59:42|对各种观点和异端观点的宽容促进了创新,而浓厚的传统观点(如中国强调中国古代的经典)则扼杀了创新
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:20:16|这样的做法有其合理之处。因为一件产品销售给千家万户,每一位顾客都有不同的使用场景,如果用玻璃瓶装的可口可乐,在一些比较正常的状态下也会发生爆炸,那就要求每一位顾客都成为专家,对气温、压强、搬动的程序等,都有比较深入的了解,否则意外就难以避免。这样,整个社会避免意外发生的成本也太高了。反过来,只要可口可乐公司能够把瓶子做得更结实一点,就能够一劳永逸地解决问题,所有顾客都不需要担心了。因此让商家承担严格责任,是有必要的。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:01:16|在整个历史上,战争常常是促进技术革新的主要因素
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:21:53|如果他的责任还是太大,既没有办法通过买保险来分摊,也没有办法通过提高商品的价格来分摊,那么他就会面临两个选择:一个是铤而走险,继续生产,一旦出了事就破产、坐牢;如果他小心谨慎一点,会做第二个选择,那就是不再生产,完全退出市场 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:02:15|强有力的集中统一的政府在19世纪后期的德国和日本对技术起了推动作用而在公元1500年后的中国则对技术起了抑制作用
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:22:08|但是如果责任人事实上是没有能力解决问题、避免意外的,他会怎么办?他的一个合理做法是去买保险,将风险分摊给其他人。与此同时,他也可以提高产品的售价,把自己所要承担的风险重新分摊给所有顾客,让所有顾客一起来承担 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:04:24|许多北欧人认为,在气候条件严峻的地方,技术能够繁荣发展,因为在那里没有技术就不能生存,而在温和的气候下,技术则会枯萎凋零,因为那里不需要穿衣,而香蕉大概也会从树上掉下来。一种相反的观点则认为,有利的环境使人们用不着为生存进行不懈的斗争,而可以一门心思地去从事创新活动
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:24:00|大家会说提高食品和药品的质量总是好的,但经济学的分析告诉我们,凡事都有成本。与销售利润没有任何牵连的审批官员,他们确实不会像商家那样追求利润,但他们追求产品的极致安全,而追求产品的极致安全,本身是有成本的 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:07:53|现在中东的伊斯兰社会相对而言比较保守并不居于技术的最前列。但同一地区的中世纪伊斯兰教社会在技术上却是先进的是能够接受新事物的。它的识字率比同时代的欧洲高得多它吸收了古典的希腊文明的遗产以致许多古典的希腊书籍只是通过阿拉伯文的译本才为我们所知它发明了或精心制作了风车、用潮水推动的碾磨、三角学和大三角帆它在冶金术、机械工程、化学工程和灌溉方法等方面取得了重大的进步它采用了中国的纸和火药又把它们传到欧洲。在中世纪技术绝大多数是从伊斯兰世界流向欧洲而不是像今天那样从欧洲流向伊斯兰世界。只是在公元1500年左右以后技术的最终流向才开始逆转
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:25:45|美国食品药品监督管理局Food and Drug Administration, FDA为例 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:14:09|一个著名的例子是日本放弃枪支。火器在公元1543年到达日本当时有两个葡萄牙人携带火绳枪(原始的枪)乘坐一艘中国货船抵达。日本人对这种新式武器印象很深于是就开始在本地制造从而大大地改进了枪支制造技术到公元1600年已比世界上任何其他国家拥有更多更好的枪支。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:26:02|1962年当时的美国总统约翰·肯尼迪还专门给FDA的局长颁发了一枚勋章以表彰FDA的贡献 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:15:19|只是因为日本是一个人口众多的孤立的海岛它才没有因为拒绝这种具有巨大作用的新军事技术而受到惩罚。1853年美国海军准将佩里率领装备有许多大炮的舰队访问日本使日本相信它有必要恢复枪支的制造直到这时日本因孤立而得到安全的状况才宣告结束
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:43:33|奥地利学派认为,要避免经济周期最根本的办法就是政府抑制住乱印钞票的冲动。关于如何抑制政府乱印钞票,奥地利学派提出了两个基本的主张:第一是要回到金本位,第二是实施自由发钞制度。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:15:46|日本拒绝枪支和中国抛弃远洋船只(以及抛弃机械钟和水力驱动纺纱机),是历史上孤立或半孤立社会技术倒退的著名例子
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 11:46:00|超超发 —>利率下降—>投资增加—>信心增加—>虚假繁荣 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:40:57|盗贼统治者和英明政治家的区别,强盗贵族和公益赞助人的区别,只是程度不同而已:这只是一个从生产者那里榨取来的财物有多少被上层人物留下来的问题,是平民对把重新分配的财物用于公共目的喜欢到什么程度的问题。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:21:10|没有哪个社会能忍受日益恶化的通货膨胀,政府迟早会采取行动,收缩货币的发行量。这时,当初人们制订的那些长远规划就找不到足够的资金来维持,资金链断裂、泡沫破灭了。这就是奥地利学派理解的经济周期发生的过程。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:42:32|为什么平民会容忍把他们艰苦劳动的成果奉送给盗贼统治者?从柏拉图到马克思的所有政治理论家都提出过这个问题,在现代的每一次选举中选民们又重新提出了这个问题
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:46:37|凯恩斯主义学派的基本主张是,政府应该逆经济周期而行,人们都在消费时政府就少采取动作;人们都不消费时,政府就应该积极消费、积极投资、积极生产 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:42:49|得不到公众支持的盗贼统治者有被推翻的危险,不是被受压迫的平民所推翻,就是被暴发的想要取而代之的盗贼统治者所推翻,这些新贵们用许诺为被窃取的果实提供更多服务的办法来谋求公众的支持
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:46:59|这里要说明的是,凯恩斯本人其实是深信市场自行调节功能的,他指出一旦社会回到正常的状态,政府就应该停止干预,让市场机制自己发挥作用。换句话说凯恩斯主义者关心的是要解决短期内出现的问题,尤其是短期内出现的失业问题,这时创造需求、制造就业机会就是政府的责任 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:43:25|从古至今的盗贼统治者混合使用了4种办法
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:48:31|货币政策是通过发钞的办法影响人们的预期,使得就业得以增加。当然发钞会引起通货膨胀,但早期的凯恩斯主义者相信在通货膨胀和失业率之间存在着替代关系,多发钞失业率就会降低,但后来发现这一招不管用了,市场出现了通货膨胀率和失业率同时高涨——滞胀——的情况。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:46:10|盗贼统治者为了得到公众支持而使用的最后一个方法,是制造一种为盗贼统治辩护的意识形态或宗教
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:50:33|超发 通胀 刺激经济 提高就业 . 滞涨破坏这个逻辑 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:47:58|解除平民的武装,同时武装上层掌权人物。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 24:59:05|把今天的责任推给将来的人,这么做可行吗?这个国家长远该怎么办?凯恩斯的经典回答是:“从长远而言,我们都将一命呜呼。”他的意思是,情况很特殊,这次不一样,管不了那么远的事了。当然,人们随时都可以把“这次不一样”作为违反原则的理由 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:48:05|用通行的方法把得到的财物的很大一部分再分配给群众来博取他们的欢心
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:03:09|弗里德曼的名言是“通货膨胀到处以及永远都是一种货币现象。”Inflation is everywhere and always a monetary phenomenon. HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:48:39|共同的意识形态或宗教有助于解决没有亲属关系的人们应如何共处而不致互相残杀这个问题——办法就是为他们规定一种不是以亲属关系为基础的约束。第二个好处是,它使人们产生了一种为别人而牺牲自己生命的动机,而不是产生利己之心
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:10:04|货市主义者认为,当新增的货币流动到社会一个角落以后,除了物价水平上涨,其他任何事情都不会改变。长期而言,政府通过货币政策来干预经济的努力是无效的。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 12:58:37|所有这些例子都表明,战争或战争威胁在大多数(即使不是全部)社会合并中起了关键的作用。但是战争,甚至仅仅是族群间的战争,一直是人类社会一个恒久不变的事实
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:17:31|既然通货膨胀无论何时何地都是一种货币现象,那怎样才能抑制政府乱发钞票的冲动呢? 奥地利学派认为,要回到金本位或者要实施自由发钞制度。而弗里德曼提出的办法是要盯死货币发行增量。哪个政府都会忍不住乱发钞票,所以解决的办法是把货币增长比例写到宪法里去,谁也改不了。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:08:42|,为什么澳大利亚土著仍然不知金属工具为何物,而仍然生活在石器时代?
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:23:36|如果社会上的失业率高涨,政府就去制造通货膨胀来对付失业率,那么第一次、第二次人们会真的以为经济变好了,开始增加投资多雇人,但试过一两次以后,人们就会形成更明确的预期,知道这只不过是政府对付眼前问题的花招,他们再做投资和雇人决策时,就会格外小心。社会上这样的人多了,政府的政策就会失效。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:46:03|全世界6000种语言中有1000种挤在一个只比得克萨斯州稍大一点的地区里分成几十个语族以及一些就像英语和汉语那样不同的互相独立的语言
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:23:53|同样的道理,当政府看到社会上消费不足,发钱让人们消费时,人们就会逐渐明白,“政府发的钱不是白给的,迟早会通过增加税收的方式要回去,所以哪怕是拿了政府的钱也不能乱花”。这时政府的政策又失效了。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 10:46:07|新几内亚是世界上语言最集中的地方
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:24:14|政府的政策赖以形成的经济模型,其中的参数是会随着人们预期的改变而改变的。当人们一旦形成预期,那些模型就不起作用了 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:30:58|贸易和战争交替进行,而战争的目的则是割取敌人的首级做战利品和把女人捉来做老婆。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:26:09|但真实经济周期理论却认为,经济学家所说的外生变量、冲击,其实是无所不在的,发生意外的冲击是我们真实生活里的常态。 干旱、地震、飓风、洪水,当然是一种冲击;新的科技发明、新的技术创新、新的市场营销手段、新的支付和结算方式,也是一种冲击;新的观念、风尚、潮流,也是冲击。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:34:45|欧洲移民的人数始终很少,今天新几内亚的人口仍然以新几内亚人为主。这同澳大利亚、美洲和南非的情况形成了鲜明的对比,因为在那些地方,欧洲人的殖民地数量多、时间久,在广大地区内取代了原来的土著人口。为什么新几内亚却不同呢?
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:27:55|如果在这以前,政府觉得出现了闲置的劳动力、出现了失业,应该管一管,从而刻意制造一些本来市场不需要的岗位让人们去做,让多余的劳动力去填充,这实际上是造成了浪费,妨碍了人们去寻找真正有价值的工作机会。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:34:58|在这些未能实现的对低地地区殖民的计划中最雄心勃勃的计划是法国侯爵德雷伊于1880年左右在附近的新爱尔兰岛组织的结果1000个殖民者在不到3年的时间里死掉了930人
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:28:55|生活中到处都是冲击,要应付这些冲击,人们的反应不是即时的,而是滞后的,不是全面的,而是渐进的。这本身就是合理的。额外的干预不会解决问题,而只会产生新的问题 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 11:49:56|欧洲人的拓殖用两种办法减少了土著的人数。一个办法就是开枪把他们打死在19世纪和18世纪晚些时候欧洲人认为这是一种可以接受的选择到20世纪30年代他们进入新几内亚高原地区时他们就很少这样考虑了。最后一次大规模的屠杀于1928年发生在艾利斯斯普林斯共杀死了31个土著。另一个办法就是欧洲人引进的病菌对这些病菌土著居民还没有机会获得免疫力或形成自然的抵抗力。1778年第一批欧洲移民到达悉尼不到一年死于流行病的土著居民的尸体便随处可见。有案可查的主要的致命疾病有天花、流行性感冒、麻疹、伤寒、斑疹伤寒、水痘、百日咳、肺结核和梅毒。 在所有适于欧洲人发展粮食生产的地区独立的土著社会就被用这两种办法消灭了。唯一的或多或少完好无损地幸存下来的社会是对欧洲人无用的澳大利亚北部和西部地区的社会。在欧洲人殖民的一个世纪内有4万年历史的土著传统基本上被消灭殆尽
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:29:16|一个人得了病,去住院是正常的,住院当然是劳动力的一种浪费,但产生这种浪费的根源——得病——早就已经发生了,再阻止这个人去住院是于事无补的。所以不让那些本来应该失业的人失业,那也是错的,错上加错并不能变成对。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/29 13:14:53|“中国戎夷,五方之民,皆有性也,不可推移。东方曰夷,被发文身,有不火食者矣。”这位周朝的作者接着又把南方、西方和北方的原始部落说成是沉溺于同样野蛮的习俗:“南方曰蛮,雕题交趾,有不火食者矣。西方曰戎,被发衣皮,有不粒食者矣。北方曰狄,衣羽毛穴居,有不粒食者矣。”
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:32:49|凯恩斯不是职业经济学家而是英国上流社会的名人、记者、政府官员和投资家。他在1936年出版了《就业、利息和货币通论》从此开创了宏观经济学这个学术分支。这本书到今天已经有80多年的历史了但是你到美国的书店去看不管书店有多大不管经济学的书架有多小里面一定还放着凯恩斯的这本《就业、利息和货币通论》 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:04:32|事实上,正是由于欧洲是分裂的,哥伦布才成功地第五次在几百个王公贵族中说服一个来赞助他的航海事业。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:33:57|凯恩斯的直觉非常准,他看到了这一点,于是就用他独特的语言来描述这种现象。在他的著作里,这种难以名状、难以刻画、难以衡量的不确定性被称之为“动物精神”。他说正是由于这些莫名其妙的原因,人们的需求忽然下降了,经济不发展了,而解决问题的办法就是要鼓励人们去消费 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:04:41|哥伦布曾请求国王派船让他向西航行探险。他的请求被国王拒绝了,于是他就求助于梅迪纳-塞多尼亚公爵,也遭到了拒绝,接着他又求助于梅迪纳-塞利伯爵依然遭到拒绝最后他又求助于西班牙的国王和王后他们拒绝了他的第一次请求但后来在他再次提出请求时总算同意了。如果欧洲在这头3个统治者中任何一个的统治下统一起来它对美洲的殖民也许一开始就失败了。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:34:11|当人们面对不确定性的冲击,当他们发现用精准的数学根本无力招架、无法应对的时候,他们就只能诉诸直觉、比喻、经验、自信以及勇气。这是宏观经济学真正深不可测的原因 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:02|对于欧洲的大炮、电灯照明、印刷术、小型火器和无数的其他发明,情况也是如此:每一项发明在欧洲的一些地方由于人们的习性起先或者被人忽视,或者遭人反对,但一旦某个地区采用了它,它最后总能传播到欧洲的其余地区
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:37:08|从长期看,通货膨胀和失业之间并不存在稳定的替代关系,也就是说不可能通过制造通货膨胀来降低失业率。 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:09|欧洲分裂所产生的这些结果与中国统一所产生的结果形成了鲜明的对比
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 13:37:23|从长期看,货币增长率决定了通货膨胀率,滥印钞票迟早会造成通货膨胀 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:05:22|除了作出停止海外航行的决定外中国的朝廷还作出停止其他一些活动的决定放弃开发一种精巧的水力驱动的纺纱机在14世纪从一场产业革命的边缘退了回来在制造机械钟方面领先世界后又把它拆毁或几乎完全破坏了以及在15世纪晚期以后不再发展机械装置和一般技术。统一的这些潜在的有害影响在现代中国又死灰复燃特别是20世纪60年代和70年代“文化大革命”中的那种狂热当时一个或几个领导人的决定就把全国的学校系统关闭了5年之久。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:25:39|到了1992年联邦储备局波士顿分行发布了一份所谓的科学统计报告声称经过严密的计算美国商业银行肯定是对弱势群体进行了歧视。 实际上,商业银行迫于竞争,并不会随便歧视弱势群体。如果真的还得起房贷,而银行不给贷款,这岂不是银行的损失?但是大众并不相信。这份报告出来以后,整个社会掀起了一场要求对弱势群体发放更多贷款的运动 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:06:19|欧洲内部的分歧今天在继续挫败甚至是想要通过欧洲经济共同体(EEC)来实现欧洲统一的并不过分的企图,这就表明欧洲对分裂的根深蒂固的执著
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:26:48|成本是放弃了的最大代价 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:06:39|中国自有文字以来就一直只有一种书写系统长期以来只有一种占支配地位的语言以及2000年来牢固的文化统一
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:27:16|沉没成本不是成本 HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:09:09|我用本书的很大篇幅着重讨论了在没有难以克服的障碍的情况下技术的传播问题。但中国在地理上的四通八达最后却成了一个不利条件,某个专制君主的一个决定就能使改革创新半途而废,而且不止一次地这样做了。相比之下,欧洲在地理上的分割形成了几十个或几百个独立的、相互竞争的小国和发明创造的中心。如果某个国家没有去追求某种改革创新,另一个国家会去那样做的,从而迫使邻国也这样去做,否则就会被征服或在经济上处于落后地位。欧洲的地理障碍足以妨碍政治上的统一,但还不足以使技术和思想的传播停止下来。欧洲还从来没有哪一个专制君王能够像在中国那样切断整个欧洲的创造源泉。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:27:28|一块地不仅可以用来建采石场或者建住宅小区它还有好多其他用途A、B、C、D、E……当一个资源有若干个选项时被选中的那个选项它的成本就是所有放弃了的选项当中价值最高的那个。简言之成本就是放弃了的最大代价。Cost is the best opportunity foregone. HL|枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)|贾雷德·戴蒙德|2020/1/30 12:11:53|新月沃地和中国的历史还为现代世界留下了一个有益的教训:环境改变了,过去是第一并不能保证将来也是第一。人们甚至会怀疑,本书从头到尾所运用的地理学推论在现代世界上是否终于变得毫不相干,因为思想可以在因特网上立即向四处传播,而货物照例可以一下子从一个洲空运到另一个洲。看来,对全世界各民族之间的竞争已实行了一些全新的规则,结果,像朝鲜、马来西亚,尤其是日本这些新的力量出现了。 然而仔细想来我们发现这些所谓的新规则不过是旧规则的改头换面而已。不错1947年美国东部贝尔实验室发明的晶体管跃进8000英里到日本去开创了电子工业——但它却没有跃进得近一些到扎伊尔或巴拉圭去建立新的工业。一跃而成为新兴力量的国家仍然是几千年前就已被吸收进旧有的以粮食生产为基础的最高权力中心的那些国家要不就是由来自这些中心的民族重新殖民的那些国家。与扎伊尔或巴拉圭不同日本和其他新兴力量之所以能够迅速利用晶体管是因为它们的国民已在文字、金属机械和中央集权的政府方面有了悠久的历史。世界上两个最早的粮食生产中心——新月沃地和中国仍然支配着现代世界或者是通过它们的一脉相承的国家(现代中国),或者是通过位于很早就受到这两个中心影响的邻近地区内的一些国家(日本、朝鲜、马来西亚和欧洲),或者是通过由它们的海外移民重新殖民或统治的那些国家(美国、澳大利亚、巴西)。撒哈拉沙漠以南的非洲人、澳大利亚土著和美洲印第安人支配世界的前景仍然显得黯淡无光。公元前8000年时的历史进程之手仍然在紧紧抓住我们。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:28:33|沉没成本,就是指那些已经发生但不可收回的支出。当我们没办法再收回、没办法再放弃时,就不存在成本。凡是提到成本,我们一定是向前(未来)看,而不是向后(过去)看的。所以,沉没成本不是成本。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:19|负面的感受不是成本
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:28|比如我们要在自家院子里修一个游泳池,修游泳池的过程,有许多负面感受:辛苦、劳累、一段时间的脏乱差等,但这些都不是修游泳池的成本,因为没有放弃什么东西。修了游泳池,这个地方就不能搭帐篷,那个放弃了的帐篷才是我们修游泳池的成本。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:30:48|你的成本由别人决定
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:31:23|如果有人愿意出2万元钱租这个铺位那么坚持卖茶叶蛋的成本就是2万元如果有人愿意出3万元那么成本就是3万元跟这房子是谁的没有关系。坚持卖茶叶蛋的成本只跟一个因素有关系那就是放弃了的最大收入。 你
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:07|一个青年,被征去当兵以后,就不能从事他原来的职业了。这时虽然多了一个廉价的士兵,但可能少了一位化学家、一个小提琴手,或是一位企业家。总的来说,义务兵制的成本是非常高的,因为它放弃的代价是不可估量的。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:21|义务征兵时,政府付出的货币成本确实比较低,但是他没有看到另外一个重要成本,就是放弃了的最大代价。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:33:37|给士兵发薪水比免费征兵更便宜
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:35:21|经济学家米尔顿·弗里德曼曾经给美国政府提过不少建议,大多数都没被接受。但是这一条,建议将征兵制改为志愿兵制,就被美国政府接受了。这对于减少美国的国防总成本,提高征兵效率,提高兵员质量,有极大的帮助。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:36:23|在大多数情况下,中间商在帮助我们减少总成本,而不是增加总成本,而中间商之间的竞争,会使物流的总成本降到最低。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:40:39|亏损会降低资源未来使用的成本
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:40:45|盈利提高了资源未来使用的成本
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:41:16|无论是盈利还是亏损,在经济学里,都是意外发生的。每当发生意外,我们就重新调整资源的未来估值,而资源使用的成本,就要按照这个新的估值来计算。所以,一旦出现盈利,资源的使用成本就会提高;一旦出现亏损,资源的使用成本就会下降。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:41:57|供需关系决定商品价格,商品价格决定资源成本
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:42:24|把钱赚到手以后,再把赚到的钱倒过来归结到前面每一个环节的生产要素里面去,从而给这些生产要素定价,决定它们的价值。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:45:11|政府无论是免费把土地送出去,还是高价拍卖土地,对最终的房价都不会造成影响。
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:46:51|不对 供需决定价格 土地供给充裕意味着房子供给充裕 怎么不导致房价降低?
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:47:08|经济学里有一个非常重要的概念,叫“租”。所谓租,就是对资产的付费
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:47:18|只要能够带来收入的资源都叫资产,而对资产的付费就叫租。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:48:31|他当歌星的收入减10万元减20万元减500万元减900万元他都照样当歌星。这990万元是他白赚的这就叫租。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:49:09|赚多赚少都是白得的,这就叫租。当然,有时候我们也把它叫“垄断租”,因为它是在数量管制的保护之下从事的一种垄断经营。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:51:04|只要能够带来收入的资源都叫资产,而对资产的付费就叫租
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:53:40|科斯的看法则别具一格,他说所有的伤害都是相互的,我们得用新的眼光来看待这些案例:不是一方在伤害另外一方,而是双方为了不同的用途,在争夺相同的稀缺的资源:牛跟小麦争的是那块地,如果让牛吃小麦,那牛就伤害了小麦;但如果禁止牛吃小麦,小麦就伤害了牛。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:54:09|关于科斯理论的争论法律经济学者、芝加哥大学法学院教授理查德·爱泼斯坦Richard Epstein曾提出过一个观点。他说如果争夺资源的双方是同一个人那会发生什么情况以这样的角度来重新审视前面提到的案子可能就会豁然开朗了。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:54:15|例如牛跟小麦之争,假设牛跟小麦同属一人,这时,牛能不能吃小麦,就取决于牛肉能卖多少钱,小麦能卖多少钱。如果小麦价格高,牛肯定不能随便吃小麦;但如果牛肉价格足够高,牛当然可以吃小麦,不仅可以吃小麦,还要给它听莫扎特的音乐,给它按摩呢。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:55:16|谁避免意外成本最低,谁的责任就最大
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:55:46|这个想法非常重要。正是基于这个想法,科斯的意思是说,火星烧着了亚麻,但是责任可能在农夫,虽然农夫并未招惹铁路公司。谁付出的成本更低,谁就应该承担更大的责任。那既然农夫避免意外所要付出的成本,比铁路公司避免意外所要付出的成本低得多,那挪开亚麻的责任,就要落到农夫身上了
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:57:03|科斯定律最流行的版本是:在交易费用为零或足够低的情况下,不管资源最初的主人是谁,资源都同样会流到价值最高的用途上去。用大白话来说,就是“谁用得好就归谁”。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:57:23|钻石归矿工还是白富美
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 14:59:13|这就引发了一场关于狼的争论。最后人们从科斯定律的角度找到了解决办法那就是养牲口的人如果能证明自己的牲口被狼咬死了那么养狼的人就需要向他提供一定数目的赔偿。从此养狼的人就知道养狼是有代价的他们必须把狼群的数目控制在一个合理的范围内我们通常把这个价叫作“科斯对价”Coasian payment。就这样一个是否应该养狼的问题一个非黑即白的问题就转变为应该养多少狼、谁来监控、谁来负责的问题也就是一个关于如何分配和使用资源的问题。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:01:09|我们说科斯定律最流行的一个版本是:在交易费用为零或足够低的情况下,不管产权归谁,资源都会落到最有价值的用途上。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:02:42|但现实生活中交易费用很高,很多资源是没有办法落到使用价值更高的人手里的。因此,初始的产权分配、制度法规、风俗习惯,乃至道德规范,就都变得很重要了。我们可以把社会上通行的制度、习俗和道德规范,都看成为了减少重复商议的成本而逐渐固定下来的解决纠纷的办法。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:03:07|而这就是所有的制度、风俗、习惯,以至政府、法院存在的根本理由——对资源、责任、权利进行初始的界定。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:04:36|另外一个问题就是不合作的所有者也就是所谓的“钉子户”。根据《强制销售条例》只要不可分割的物业的90%以上的所有者同意出售那么剩下的10%的业主就必须出售他的土地和相应的物业。2010年这个条例又做了修改比例从90%下降到80%。当然,里面还加了一条,要进行这种征地,必须经过特区行政长官的批准。 到底是90%合理还是80%更合理呢?我们不可能有一个科学的界定。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:07:10|这是我们寻求合作解,“不讲理,只讲数”的一个很好的例子。我们应当充分领会科斯定律的精神,在现实生活的各种冲突中积极地寻找合作解。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:25:11|当我们把口袋里有限的钱,按“边际效用等于边际成本”的原则,分别用来购买不同的产品时,我们从这些不同的产品那里获得的总收益,就会达到最大。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:33:40|我们怎么能知道不存在“价格上升需求量也上升”的例子呢?的确有过一些记载,但它们几乎全都可以理解为价格以外的某些因素发生了变化
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:37:31|一件商品是奢侈品还是必需品,完全取决于价格
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:37:51|需求曲线上的弹性处处不等,价格越高弹性越大
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:39:17|古董价格 vs 水电煤
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:39:47|掌握了商品的需求弹性会随着商品价格的变化而变化的规律,我们就容易明白一个道理:任何商品都既可以是奢侈品,也可以是必需品,这完全取决于价格的高低。当商品足够便宜的时候,它自然就成为必需品;当它贵到一定程度,它就自然变成奢侈品。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:40:16|例如,水到底是奢侈品还是必需品?一般人会说,水当然是必需品。但想想看,在水资源特别匮乏的地方,人们使用水的数量,跟水资源非常丰富的地方,是完全不一样的。水资源特别匮乏时,人们不会随便浇花、洗车、洗澡。少量的水会先洗脸再洗脚,洗完脚又刷鞋,刷完鞋还要拿来冲厕所。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:42:12|这种变化趋势就是需求第二定律所说的:随着时间的推移,需求对价格的弹性会变得越来越高,也就是说替代品将会越来越多、应变的空间会越来越大。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:45:02|这就是需求第三定律的含义:每当消费者必须支付一笔附加费时,高品质的产品相对低品质的产品就变得便宜了,这笔附加费越高,高品质产品相对就越便宜。正因为这样,我们也把这个定律叫作“好东西运到远方去定律”。
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:46:14|包装费用
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:48:56|大家都批评发国难财的行为。但我们想想,国难是那个发国难财的人造成的吗?如果不是,那么借机发财的人,其实就只是给别人多提供了一个选择罢了。发国难财的人可能确实利用了别人,但是这种利用,对另一方来说也是有好处的。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:49:25|生活中乘人之危来利用他人的现实,其实很多。比如,医生不就利用了病人生病吗?老师不就利用了学生无知吗?但是医生对病人来说是有帮助的,老师对学生来说也是有帮助的。但这个道理很多人都不明白,不管是中国人还是外国人
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:54:33|这三位经济学家一位是1992年获得诺贝尔奖的加里·贝克尔Gary Becker他说发国难财是增加供给的最好办法当然应该鼓励。第二位经济学家是2002年诺奖得主弗农·史密斯Vernon Smith他说发国难财是好事。第三位经济学家是大家熟悉的1976年的诺奖得主米尔顿·弗里德曼他说“这些发国难财的人是在救别人的命他们应该得到一个奖章而不是得到惩罚。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:54:56|阻止发国难财的直接后果,就是剥夺了遭受灾害的人的选择。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:04|我们在经济学中学到的最重要的教训之一,就是把愿望和结果分开来看。愿望是一回事,但愿望造成的结果是另外一回事。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:30|一个人想要买最便宜的苹果,但是当他进入市场寻找最便宜的苹果时,他的寻找行为本身就使得苹果的价格上升了。一个人想要卖最贵的苹果,但是当他进入市场,想要把自己的苹果以最高价卖出去时,他的行为本身就已经使得苹果的价格下跌了。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 15:56:45|那些乘人之危发财的人,他们自己本身的行为就增加了供给,使商品的价格下降,缓解了供需之间的矛盾。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:00:43|回顾中国的经济改革,如果只用一句话来概括,我会说,它是有关竞争规则改变的改革:从过去看排队的时间,看出身,看政治面貌,变成另外一个规则,那就是看出价的高低。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:01:23|短缺是因为价格受到了抑制
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:01:48|春节期间买不到火车票,人们要找黄牛党才能买到,这叫短缺
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:02:46|同样的道理,我们也没有听说奔驰汽车短缺过。它只是稀缺,但不会短缺,它永远在商店里等着我们。哪怕是暂时缺货,也不会成为一个新闻,不会成为一个社会现象。蒂芙尼的钻石也一样,它永远待在商店里等待着顾客。人们只要把钱放下,就可以带它回家。我们从未看哪个媒体说,蒂芙尼的钻石短缺了
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:02:58|过剩是价格被人为拔高的结果
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:04:46|不难看出,价格管制越严重,价值耗散就越大,人们设法绕过政府管制的积极性也越大;而政府围堵的力度越大,人们采用的对策就越迂回,其中白白浪费掉的竞争成本也越大。从这个角度看,价格管制之下,形形色色的违法活动以及它们出场的次序,也都是可以理解的。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:05:49|人们常有这样的鸵鸟心态,在讨论价格的时候,会说那些正在触动神经的商品非常特殊,不是商品,所以价格规律不起作用。谈水费的时候,说水不是商品;谈学费的时候,教育不是商品;谈药费的时候,健康不是商品;谈旅费的时候,回家过年不是商品。然而,抱着这样的心态,只能让人脱离现实,而无法正视问题和寻求对策。毕竟经济规律是不以人的意志为转移的
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:06:32|反对的第二个理由是,回家过年是刚需。意思是不管价格多高,人们还是要回家过年,因此提价只会让铁路系统多赚钱,而不能降低人们回家过年的愿望。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 16:17:24|他们说:帮穷人的最好办法,就是以金钱的方式给穷人补贴,同时让市场发挥它自身应有的功能,而不是直接去干预商品的价格。

66
kman.py
View File

@@ -8,6 +8,7 @@
######################################################### #########################################################
import re import re
import sys
import os import os
import io import io
import json import json
@@ -60,20 +61,32 @@ books_data =
SYS = 'WIN' if platform.system()=='Windows' else \ SYS = 'WIN' if platform.system()=='Windows' else \
('LINUX' if platform.system()=='LINUX' else 'MAC') ('LINUX' if platform.system()=='LINUX' else 'MAC')
frozen = 'not'
#CURRPATH = os.path.dirname(os.path.realpath(sys.argv[0]))
CURRPATH = ''
if getattr(sys, 'frozen', False):
# we are running in a bundle
frozen = 'ever so'
CURRPATH = sys._MEIPASS
os.chdir(CURRPATH)
else:
# we are running in a normal Python environment
CURRPATH = os.path.dirname(os.path.abspath(__file__))
# some constants # some constants
LASTLINE = '==========' LASTLINE = '=========='
NTPREF = '--CG注:' NTPREF = '--CG注:'
CLIPFN = 'My Clippings.txt' CLIPFN = 'My Clippings.txt'
WORDFN = 'vocab.db' WORDFN = 'vocab.db'
CLIPPATH = './' OUTPREF = os.path.join(CURRPATH,'CLIP')
OUTPREF = './clip'
DEBUG = 1 # 0 - INFO; 1 - DEBUG DEBUG = 1 # 0 - INFO; 1 - DEBUG
LOG2FILE = 1 # 0 - to stdio; 1 - to file LOG2FILE = 1 # 0 - to stdio; 1 - to file
LOGFILE = 'log'
DELIMITER= '|' DELIMITER= '|'
BACKUPNOTEFN = './backup/bk.note.data' # kindle notes LOGFILE = os.path.join(CURRPATH,'log')
BACKUPWORDFN = './backup/bk.word.data' # kindle words BACKUPFOLDER = os.path.join(CURRPATH,'backup')
BACKUPINFOFN = './backup/bk.info.data' # book information from douban/amazon BACKUPNOTEFN = os.path.join(BACKUPFOLDER,'bk.note.data') # kindle notes
BACKUPWORDFN = os.path.join(BACKUPFOLDER,'bk.word.data') # kindle words
BACKUPINFOFN = os.path.join(BACKUPFOLDER,'bk.info.data') # book information from douban/amazon
#HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'} #HEADER = {0:'type',1:'bookname',2:'author',3:'position',4:'date',5:'content'}
# log info # log info
@@ -119,6 +132,7 @@ r'''
class kMan: class kMan:
def __init__(self, parent=None): def __init__(self, parent=None):
self.hlnum = 0 self.hlnum = 0
self.ntnum = 0 self.ntnum = 0
self.refleshtime = '2020/10/10 10:00:00' self.refleshtime = '2020/10/10 10:00:00'
@@ -130,9 +144,9 @@ class kMan:
kp = self.get_kindle_path() kp = self.get_kindle_path()
if not kp: if not kp:
s2 = u'Disconnected ({})'.format(CLIPPATH+CLIPFN) s2 = u'Disconnected ({})'.format(os.path.join(CURRPATH,CLIPFN))
else: else:
with open(kp+'/system/version.txt' , 'r', encoding='utf8', errors='ignore') as f: with open(os.path.join(kp,'system','version.txt'), 'r', encoding='utf8', errors='ignore') as f:
s2 = u'Connected ({}) version {}'.format(kp,f.read().strip()) s2 = u'Connected ({}) version {}'.format(kp,f.read().strip())
return [s1,s2] return [s1,s2]
@@ -310,7 +324,7 @@ class kMan:
""" """
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'} suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
op = fnpref+suff[ft] op = os.path.join(CURRPATH, fnpref+suff[ft])
with open(op, 'w', encoding='utf8', errors='ignore') as fw: with open(op, 'w', encoding='utf8', errors='ignore') as fw:
if ft=='JSON': if ft=='JSON':
@@ -344,7 +358,7 @@ class kMan:
""" """
suff = {'MD':'.md','CSV':'.csv','JSON':'.json'} suff = {'MD':'.md','CSV':'.csv','JSON':'.json'}
op = fnpref+suff[ft] op = os.path.join(CURRPATH,fnpref+suff[ft])
with open(op, 'w', encoding='utf8', errors='ignore') as fw: with open(op, 'w', encoding='utf8', errors='ignore') as fw:
if ft=='JSON': if ft=='JSON':
@@ -579,6 +593,10 @@ class kMan:
#serr = str(stream_stderr.read()) #serr = str(stream_stderr.read())
#if sout: print('stdout {}'.format(sout)) #if sout: print('stdout {}'.format(sout))
#if serr: print('stderr {}'.format(serr)) #if serr: print('stderr {}'.format(serr))
<<<<<<< HEAD
=======
#print('os.name {} sout {}'.format(os.name,sout))
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
if os.name == 'nt': # windows if os.name == 'nt': # windows
for d in sout.split('\n'): for d in sout.split('\n'):
if 'Kindle' in d: if 'Kindle' in d:
@@ -604,7 +622,7 @@ class kMan:
return False return False
def import_clips(self, fp=(CLIPPATH+CLIPFN)): def import_clips(self, fp=os.path.join(CURRPATH, CLIPFN)):
"""import clips from local file or kindle """import clips from local file or kindle
4 lines for each section seperated with '=======' 4 lines for each section seperated with '======='
so read 4 lines before '=======' so read 4 lines before '======='
@@ -623,6 +641,8 @@ class kMan:
path = fn path = fn
""" """
if not os.path.exists(fp): return {}
# loop to fill books dict # loop to fill books dict
with open(fp, 'r', encoding='utf8', errors='ignore') as f: with open(fp, 'r', encoding='utf8', errors='ignore') as f:
bks = defaultdict(dict) bks = defaultdict(dict)
@@ -677,7 +697,7 @@ class kMan:
return self.drop_duplicate(bks) return self.drop_duplicate(bks)
# words operations # words operations
def import_words(self, fp=(CLIPPATH+WORDFN)): def import_words(self, fp=(os.path.join(CURRPATH,WORDFN))):
"""import words from local file or kindle """import words from local file or kindle
vocab.db -> tables: BOOK_INFO, LOOKUPS, WORDS vocab.db -> tables: BOOK_INFO, LOOKUPS, WORDS
@@ -791,7 +811,23 @@ class kMan:
return [nu, bk_wd_num] return [nu, bk_wd_num]
class Util:
def __init__(self, parent=None):
pass
@staticmethod
def get_app_path():
"""Returns the base application path."""
if hasattr(sys, 'frozen'):
# Handles PyInstaller
return os.path.dirname(sys.executable) # 使用pyinstaller打包后的exe目录
return os.path.dirname(__file__) # 没打包前的py目录
if __name__=='__main__': if __name__=='__main__':
"""
# XXX move to unitest.kman.py
#books = defaultdict(dict) #books = defaultdict(dict)
km = kMan() km = kMan()
books = km.import_clips() books = km.import_clips()
@@ -814,12 +850,14 @@ if __name__=='__main__':
km.add_note_to_highlight(books) km.add_note_to_highlight(books)
# test dict json convert # test dict json convert
with open('./xx', 'w', encoding='utf8', errors='ignore') as fw: with open(os.path.join(CURRPATH,'xx'), 'w', encoding='utf8', errors='ignore') as fw:
fw.write(km.dict2json(books)) fw.write(km.dict2json(books))
if km.json2dict('./xx')==books: print( 'test OK') if km.json2dict(os.path.join(CURRPATH,'xx'))==books: print( 'test OK')
km.export_notes(books, OUTPREF, ft='MD') km.export_notes(books, OUTPREF, ft='MD')
# print data with json format # print data with json format
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False)) logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))
"""
pass

BIN
kmanapp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -9,11 +9,17 @@
import sys import sys
import os import os
import operator
from time import sleep from time import sleep
import pandas as pd #import pandas as pd
from mtable import mTable
import threading import threading
from PySide2.QtWidgets import (QMainWindow, QApplication, QLabel, QAbstractItemView, QHeaderView) #if hasattr(sys, 'frozen'):
# os.environ['PATH'] = sys._MEIPASS + ";" + os.environ['PATH']
from PySide2.QtWidgets import (QMainWindow, QApplication, QMessageBox,
QFileDialog, QLabel, QAbstractItemView, QHeaderView, QMenu)
from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt) from PySide2.QtCore import (QAbstractTableModel, Signal, QSize, QTimer, Qt)
from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon) from PySide2.QtGui import (QPalette, QStandardItemModel, QStandardItem, QIcon)
@@ -60,6 +66,7 @@ words_temp = """<br><span style='font-size:18pt;color:#31849B'>{usage}</span><br
ONLY_TEST = 1 ONLY_TEST = 1
class kmanWindow(QMainWindow): class kmanWindow(QMainWindow):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(kmanWindow, self).__init__(*args, **kwargs) super(kmanWindow, self).__init__(*args, **kwargs)
@@ -68,6 +75,8 @@ class kmanWindow(QMainWindow):
def __init__(self, parent=None): def __init__(self, parent=None):
super(kmanWindow, self).__init__(parent) super(kmanWindow, self).__init__(parent)
self.copyinfo = ''
# create ui and initial it # create ui and initial it
ui = Ui_MainWindow() ui = Ui_MainWindow()
ui.setupUi(self) ui.setupUi(self)
@@ -81,6 +90,13 @@ class kmanWindow(QMainWindow):
self.km = kMan() self.km = kMan()
self.spide = bookInfoSpide() self.spide = bookInfoSpide()
<<<<<<< HEAD
=======
### in order to smaller the package,
### substitute pandas table with mTable
self.mt = mTable()
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
self.books_info = defaultdict(dict) self.books_info = defaultdict(dict)
# initial check order: # initial check order:
# 1. backup file bk.data -> # 1. backup file bk.data ->
@@ -90,9 +106,8 @@ class kmanWindow(QMainWindow):
if os.path.exists(BACKUPNOTEFN) and os.path.exists(BACKUPWORDFN): if os.path.exists(BACKUPNOTEFN) and os.path.exists(BACKUPWORDFN):
self.books_data = self.km.json2dict(BACKUPNOTEFN) self.books_data = self.km.json2dict(BACKUPNOTEFN)
self.words_data = self.km.json2dict(BACKUPWORDFN) self.words_data = self.km.json2dict(BACKUPWORDFN)
if (len(self.books_data)*len(self.words_data[0]))>=1: if self.books_data and len(self.books_data) >=1 and \
self.books_data = self.km.json2dict(BACKUPNOTEFN) self.words_data and len(self.words_data[0])>=1:
self.words_data = self.km.json2dict(BACKUPWORDFN)
flg = 1 flg = 1
if not flg: if not flg:
@@ -114,7 +129,10 @@ class kmanWindow(QMainWindow):
# if the information exist in backup file, initial with this file, # if the information exist in backup file, initial with this file,
# and grap new book's information from douban # and grap new book's information from douban
# else grap all book information from douban # else grap all book information from douban
<<<<<<< HEAD
""" """
=======
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0
try: try:
if os.path.exists(BACKUPINFOFN): if os.path.exists(BACKUPINFOFN):
self.books_info = self.km.json2dict(BACKUPINFOFN) self.books_info = self.km.json2dict(BACKUPINFOFN)
@@ -155,7 +173,8 @@ class kmanWindow(QMainWindow):
#ui.searchLineEdit.returnPressed.connect(self.search_return_press()) #ui.searchLineEdit.returnPressed.connect(self.search_return_press())
ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change) ui.searchComboBox.currentIndexChanged.connect(self.search_scope_change)
ui.searchToolButton.clicked.connect(self.search_button_clicked) ui.searchToolButton.clicked.connect(self.search_button_clicked)
ui.treeView.clicked.connect(self.tree_item_clicked) #ui.treeView.clicked.connect(self.tree_item_clicked)
ui.treeView.selectionModel().selectionChanged.connect(self.tree_item_selectionchanged)
ui.tableView.clicked.connect(self.table_item_clicked) ui.tableView.clicked.connect(self.table_item_clicked)
ui.tableView.horizontalHeader().setStretchLastSection(True) ui.tableView.horizontalHeader().setStretchLastSection(True)
#ui.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents) #ui.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
@@ -170,7 +189,11 @@ class kmanWindow(QMainWindow):
ui.tablemodel = nTableModel(data) ui.tablemodel = nTableModel(data)
ui.tableView.verticalHeader().hide() ui.tableView.verticalHeader().hide()
ui.tableView.setModel(self.ui.tablemodel) ui.tableView.setModel(self.ui.tablemodel)
ui.tableView.setSortingEnabled(True)
ui.tableView.selectionModel().selectionChanged.connect(self.table_item_selectionchanged)
#ui.tableView.contextMenuEvent().selectionChanged.connect(self.table_item_selectionchanged)
# XXXX
self.ui.textEdit.installEventFilter(self) self.ui.textEdit.installEventFilter(self)
# XXXX # XXXX
@@ -190,21 +213,6 @@ class kmanWindow(QMainWindow):
else: else:
super(kmanWindow, self).eventFilter(source, event) super(kmanWindow, self).eventFilter(source, event)
"""
app = QApplication(sys.argv)
editor = MyWidget()
cursor = editor.textCursor()
fmt = cursor.charFormat()
fmt.setForeground(QColor('blue'))
address = 'http://example.com'
fmt.setAnchor(True)
fmt.setAnchorHref(address)
fmt.setToolTip(address)
cursor.insertText("Hello world again", fmt)
editor.show()
app.exec_()
"""
def check_increase_books(self, bks, bksinfo): def check_increase_books(self, bks, bksinfo):
new_list = list(bks.keys()) # kindle's books with note new_list = list(bks.keys()) # kindle's books with note
new_list = [re.split(r'[\(\-\:_\s]',nn.strip())[0] for nn in new_list] new_list = [re.split(r'[\(\-\:_\s]',nn.strip())[0] for nn in new_list]
@@ -252,6 +260,7 @@ class kmanWindow(QMainWindow):
self.ui.tableView.verticalHeader().hide() self.ui.tableView.verticalHeader().hide()
self.ui.tableView.setModel(self.ui.tablemodel) self.ui.tableView.setModel(self.ui.tablemodel)
#self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot) #self.ui.tablemodel.tabledata_update.connect(self.tabledata_update_slot)
self.ui.tableView.selectionModel().selectionChanged.connect(self.table_item_selectionchanged)
# refresh textedit content # refresh textedit content
if comp in [1,2]: if comp in [1,2]:
@@ -261,6 +270,11 @@ class kmanWindow(QMainWindow):
note=stype, content=scontent, position=sposition) note=stype, content=scontent, position=sposition)
print(re.split(r'[\(\-\:_\s]',sbookname.strip())[0]) print(re.split(r'[\(\-\:_\s]',sbookname.strip())[0])
self.ui.textEdit.setHtml(tt) self.ui.textEdit.setHtml(tt)
self.copyinfo = \
""" {content} \n\n--《{bookname}{author} ({time}) 【{note} P{position}""". \
format( content=scontent, bookname=sbookname, author=sauthor, time=stime,
note=stype, position=sposition )
elif comp in [3,4]: elif comp in [3,4]:
self.render_textedit_words(self.words_data) self.render_textedit_words(self.words_data)
elif comp == 5: elif comp == 5:
@@ -278,6 +292,7 @@ class kmanWindow(QMainWindow):
word = index.sibling(index.row(), 0).data() word = index.sibling(index.row(), 0).data()
txt = "" txt = ""
self.copyinfo = ''
for row in lookups: for row in lookups:
if words[row[1]]['word'] == word: if words[row[1]]['word'] == word:
[susage, stimestamp, sbookname, sauthor, scategory, sposition] = \ [susage, stimestamp, sbookname, sauthor, scategory, sposition] = \
@@ -289,6 +304,12 @@ class kmanWindow(QMainWindow):
author=sauthor,category=scategory, author=sauthor,category=scategory,
timestamp=stimestamp,position=sposition) timestamp=stimestamp,position=sposition)
self.copyinfo += \
"""{usage} \n\n-- {bookname} {author} {category} {timestamp} {position} \n """ \
.format( usage=susage,bookname=sbookname, \
author=sauthor,category=scategory, \
timestamp=stimestamp,position=sposition)
self.ui.textEdit.setHtml(txt) self.ui.textEdit.setHtml(txt)
''' '''
@@ -317,30 +338,58 @@ class kmanWindow(QMainWindow):
self.ui.textEdit.setOpenExternalLinks(True) self.ui.textEdit.setOpenExternalLinks(True)
self.ui.textEdit.setHtml(infos_temp.format(link=vv['link'], self.ui.textEdit.setHtml(infos_temp.format(link=vv['link'],
bookname=vv['bookname'], bookname=vv['bookname'],
author=vv['author'], ratenum=vv['ratenum'], author=vv['author'],
score=vv['score'], publisher=vv['publisher'], ratenum=vv['ratenum'],
publishing=vv['publishing'],description=vv['description'], score=vv['score'],
publisher=vv['publisher'],
publishing=vv['publishing'],
description=vv['description'],
img=vv['img'].split('/')[-1])) img=vv['img'].split('/')[-1]))
self.copyinfo = """ 1 | :green_book: | [{bookname}]({link}) | """ \
"""{author}/{score}/{publisher}/{publishing} {description}""" \
.format( bookname=vv['bookname'], \
link=vv['link'],
author=vv['author'],
score=vv['score'],
publisher=vv['publisher'],
publishing=vv['publishing'],
description=vv['description'],)
else: pass else: pass
def convert_to_panda(self, mlist, tp=0): def convert_to_panda(self, mlist, tp=0):
if tp==0: if tp==0:
pdframe = pd.DataFrame(mlist, \ #### XXX remove pandas
#pdframe = pd.DataFrame(mlist, \
# columns = ['Type','Bookname','Author','Position','Date','Content'])
self.mt.dataframe(mlist, \
columns = ['Type','Bookname','Author','Position','Date','Content']) columns = ['Type','Bookname','Author','Position','Date','Content'])
else: else:
pdframe = pd.DataFrame(mlist, \ #pdframe = pd.DataFrame(mlist, \
# columns = ['Word','Bookname','Author','Category'])
self.mt.dataframe(mlist, \
columns = ['Word','Bookname','Author','Category']) columns = ['Word','Bookname','Author','Category'])
return pdframe return self.mt
def tabledata_update_slot(self, s): def tabledata_update_slot(self, s):
print('call tabledata_update_slot() {}'.format(s)) print('call tabledata_update_slot() {}'.format(s))
def tree_item_selectionchanged(self, selected, deselected):
# selected, deselected are QItemSelection objects
model_index_list = selected.indexes() # QModelIndexList
if not model_index_list: return 0
model_index = model_index_list[0] # QModelIndex
self.tree_item_clicked(model_index)
def tree_item_clicked(self, modelidx): def tree_item_clicked(self, modelidx):
# modelidx is a QModelIndex object
model_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList model_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
model_index = model_index_list[0] # QModelIndex model_index = model_index_list[0] # QModelIndex
itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
item = itemmodel.itemFromIndex(modelidx) #QStandardItem item = itemmodel.itemFromIndex(model_index) #QStandardItem
self.tree_selected = item.accessibleDescription() self.tree_selected = item.accessibleDescription()
print(self.tree_selected, item.text()) print(self.tree_selected, item.text())
@@ -351,6 +400,7 @@ class kmanWindow(QMainWindow):
comp = 0 comp = 0
if self.tree_selected.split('_')[0] == 'info': if self.tree_selected.split('_')[0] == 'info':
self.ui.tableView.setVisible(False) self.ui.tableView.setVisible(False)
self.ui.tableView.clearSelection()
self.render_textedit_infos(self.books_info, item.text()) self.render_textedit_infos(self.books_info, item.text())
comp = 5 comp = 5
else: else:
@@ -389,6 +439,13 @@ class kmanWindow(QMainWindow):
self.refresh_ui_component(comp) self.refresh_ui_component(comp)
def table_item_selectionchanged(self, selected, deselected):
# selected, deselected are QItemSelection objects
model_index_list = selected.indexes() # QModelIndexList
if not model_index_list: return 0
model_index = model_index_list[0] # QModelIndex
self.table_item_clicked(model_index)
def table_item_clicked(self, index): def table_item_clicked(self, index):
""" """
print('tableView.currentIndex().row() {} tableView.currentIndex().column() {}' print('tableView.currentIndex().row() {} tableView.currentIndex().column() {}'
@@ -410,7 +467,11 @@ class kmanWindow(QMainWindow):
self.ui.textEdit.setHtml(notes_temp.format( self.ui.textEdit.setHtml(notes_temp.format(
bookname=sbookname, author=sauthor, time=stime, bookname=sbookname, author=sauthor, time=stime,
note=stype, content=scontent, position=sposition)) note=stype, content=scontent, position=sposition))
elif ss=='note': self.copyinfo = \
""" {content} \n\n-- 《{bookname}{author} ({time}) 【{note} P{position}""". \
format( content=scontent, bookname=sbookname, author=sauthor, time=stime,
note=stype, position=sposition )
elif ss=='word':
self.render_textedit_words(self.words_data) self.render_textedit_words(self.words_data)
def search_button_clicked(self): def search_button_clicked(self):
@@ -434,6 +495,58 @@ class kmanWindow(QMainWindow):
print('call keyPressEvent() {} '.format(event.key())) print('call keyPressEvent() {} '.format(event.key()))
self.search_button_clicked() self.search_button_clicked()
def contextMenuEvent(self, event):
# do not work!?
#if(self.ui.tableView.geometry().contains(event.pos())):
# print('contextMenuEvent pos {}'.format(event.globalPos()))
tree_index_list = self.ui.treeView.selectedIndexes() # QModelIndexList
table_index_list = self.ui.tableView.selectedIndexes()
# tableview item selected
if table_index_list:
model_index = table_index_list[0] # QModelIndex
menu = QMenu()
copy_action = menu.addAction(u"Copy")
action = menu.exec_(self.mapToGlobal(event.pos()))
if action ==copy_action:
self.copy_action(model_index.row(), model_index.column())
elif tree_index_list:
model_index = tree_index_list[0] # QModelIndex
itemmodel = model_index.model() #QAbstractItemModel/QStandardItemModel
item = itemmodel.itemFromIndex(model_index) #QStandardItem
pp = self.tree_selected.split('_')[0]
menu = QMenu()
[copy_info_action, export_note_action, export_word_action] = \
[None, None, None]
if pp=='info':
copy_info_action = menu.addAction(u"Copy")
elif pp=='note':
export_note_action = menu.addAction(u"Export Notes")
elif pp=='word':
export_word_action = menu.addAction(u"Export Notes")
else: pass
action = menu.exec_(self.mapToGlobal(event.pos()))
fn = self.tree_selected+item.text().split('(')[0].strip()
if action==copy_info_action:
action = menu.exec_(self.mapToGlobal(event.pos()))
self.copy_action(model_index.row(), model_index.column())
elif action==export_note_action: self.export_filter_notes(fn)
elif action==export_word_action: self.export_filter_words(fn)
else: pass
else: pass
print('contextMenuEvent pos {}'.format(event.globalPos()))
def copy_action(self, row, column):
import clipboard
clipboard.copy(self.copyinfo)
print('copy_action row {} column {}'.format(row, column))
def search_return_press(self): def search_return_press(self):
self.search_button_clicked() self.search_button_clicked()
@@ -478,8 +591,8 @@ class kmanWindow(QMainWindow):
self.messagebox(ico=2, info='\n\n kindle is not connected') self.messagebox(ico=2, info='\n\n kindle is not connected')
return 0 return 0
self.books_data = self.km.import_clips(fp+'documents/'+CLIPFN) self.books_data = self.km.import_clips(os.path.join(fp,'documents',CLIPFN))
self.words_data = self.km.import_words(fp+'system/vocabulary/'+WORDFN) self.words_data = self.km.import_words(os.path.join(fp,'system','vocabulary',WORDFN))
[self.filter_books, self.filter_list] = self.km.filter_clips(self.books_data) [self.filter_books, self.filter_list] = self.km.filter_clips(self.books_data)
self.filter_wordlist = self.km.filter_words(self.words_data) self.filter_wordlist = self.km.filter_words(self.words_data)
@@ -649,18 +762,22 @@ class kmanWindow(QMainWindow):
pass pass
def export(self): def export(self):
if self.tree_selected.split('_')[0]=='note': pp = self.tree_selected.split('_')[0]
self.export_filter_notes() if pp=='note':
else: self.export_filter_notes('export')
self.export_filter_words() elif pp=='word':
self.export_filter_words('export')
else: # info
pass
print("call export()") print("call export()")
def export_filter_notes(self): def export_filter_notes(self, fn):
self.km.export_notes(self.filter_books, 'export', ft='MD') self.km.export_notes(self.filter_books, fn, ft='MD')
pass pass
def export_filter_words(self): def export_filter_words(self, fn):
self.km.export_words(self.words_data, self.filter_list, 'export', ft='MD') self.km.export_words(self.words_data, self.filter_list, fn, ft='MD')
pass pass
def messagebox(self, ico=1, info=''): def messagebox(self, ico=1, info=''):
@@ -687,6 +804,8 @@ class kmanWindow(QMainWindow):
# backup file when kman closed # backup file when kman closed
# so we can read backup file when kman start # so we can read backup file when kman start
def closeEvent(self, e): def closeEvent(self, e):
if not os.path.exists(BACKUPFOLDER): os.mkdir(BACKUPFOLDER)
with open(BACKUPNOTEFN, 'w', encoding='utf8', errors='ignore') as fw: with open(BACKUPNOTEFN, 'w', encoding='utf8', errors='ignore') as fw:
fw.write(self.km.dict2json(self.books_data)) fw.write(self.km.dict2json(self.books_data))
with open(BACKUPWORDFN, 'w', encoding='utf8', errors='ignore') as fw: with open(BACKUPWORDFN, 'w', encoding='utf8', errors='ignore') as fw:
@@ -749,24 +868,46 @@ class nTableModel(QAbstractTableModel):
def data(self, index, role): def data(self, index, role):
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()] #### XXX remove pandas
#value = self._data.iloc[index.row(), index.column()]
value = self._data.get_iat(index.row(), index.column())
self.tabledata_update[str].emit('{} {}'.format(index.row(),index.column())) self.tabledata_update[str].emit('{} {}'.format(index.row(),index.column()))
return str(value) return str(value)
def rowCount(self, index): def rowCount(self, index):
return self._data.shape[0] #### XXX remove pandas
#return self._data.shape[1]
return self._data.get_num_rows()
def columnCount(self, index): def columnCount(self, index):
return self._data.shape[1] #### XXX remove pandas
#return self._data.shape[1]
return self._data.get_num_columns()
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
# section is the index of the column/row. # section is the index of the column/row.
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
if orientation == Qt.Horizontal: if orientation == Qt.Horizontal:
return str(self._data.columns[section]) #### XXX remove pandas
#return str(self._data.columns[section])
return str(self._data.get_columns()[section])
if orientation == Qt.Vertical: if orientation == Qt.Vertical:
return str(self._data.index[section]) #### XXX remove pandas
#return str(self._data.index[section])
return str(self._data.get_index()[section])
# cheer!!!
def sort(self, column, order):
"""Sort table by given column number.
"""
self.layoutAboutToBeChanged.emit()
self._data.set_data(sorted(self._data.get_data(), key=operator.itemgetter(column)))
if order == Qt.DescendingOrder:
self._data.reverse()
self.layoutChanged.emit()
if __name__ == "__main__": if __name__ == "__main__":
@@ -795,7 +936,7 @@ if __name__ == "__main__":
""" """
# loop check kindle is connected or not # loop check kindle is connected or not
# BUG to be implement XXXX # BUG, to be implement XXXX
""" """
try: try:
t = threading.Thread(target=kmw.check_kindle_status) t = threading.Thread(target=kmw.check_kindle_status)
@@ -804,4 +945,5 @@ if __name__ == "__main__":
print ("Error: can not start thread") print ("Error: can not start thread")
""" """
app.exec_() app.exec_()
#sys.exit(app.exec_())

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject> <!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.2, 2020-06-20T21:18:52. --> <!-- Written by QtCreator 4.11.2, 2020-06-29T09:50:04. -->
<qtcreator> <qtcreator>
<data> <data>
<variable>EnvironmentId</variable> <variable>EnvironmentId</variable>

View File

@@ -54,6 +54,5 @@
<file>icons/book.png</file> <file>icons/book.png</file>
<file>icons/amazon.png</file> <file>icons/amazon.png</file>
<file>icons/flower.png</file> <file>icons/flower.png</file>
<file>kmanapp.ico</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -1,3 +1,4 @@
<<<<<<< HEAD
# -*- mode: python ; coding: utf-8 -*- # -*- mode: python ; coding: utf-8 -*-
block_cipher = None block_cipher = None
@@ -38,3 +39,46 @@ coll = COLLECT(exe,
upx=True, upx=True,
upx_exclude=[], upx_exclude=[],
name='kmanapp') name='kmanapp')
=======
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py', ],
pathex=['/Users/mark/kman'],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
cipher=block_cipher)
exe = EXE(pyz,
a.scripts,
[],
exclude_binaries=True,
name='kmanapp',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False , icon='kmanapp.ico')
coll = COLLECT(exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='kmanapp')
app = BUNDLE(coll,
name='kmanapp.app',
icon='kmanapp.ico',
bundle_identifier=None)
>>>>>>> 2d53124ab9aa26c9733646348174311d0967c8c0

File diff suppressed because it is too large Load Diff

45
makepkg.md Normal file
View File

@@ -0,0 +1,45 @@
# 打包问题
1. 'ValueError: too many values to unpack (expected 2)
A: [资源文件打包配置](https://blog.csdn.net/weixin_42052836/article/details/82315118)
资源文件包括打包的python项目使用的相关文件如图标文件文本文件等。对于此类资源文件的打包需要设置Analysis的datas如例子所示datas接收元组datas=[(SETUP_DIR+'lib\\icon','lib\\icon'),(SETUP_DIR+'data','data')]。元组的组成为(原项目中资源文件路径,打包后路径),例子中的(SETUP_DIR+'lib\\icon','lib\\icon')表示从D:\\install_test\\FASTPLOT\\lib\\icon下的图标文件打包后放入打包结果路径下的lib\\icon目录。
1. (kmanenv) [gavin@gavin-2 tkman]$open Release/kmanapp.app/
FAIL - LSOpenURLsWithRole() failed with error -10810 for the file /Users/mark/penv/tkman/Release/kmanapp.app.
(kmanenv) [gavin@gavin-2 tkman]$Release/kmanapp.app/Contents/MacOS/kmanapp
OK
A:
1. 到其他路径区执行(kmanenv) [gavin@gavin-2 penv]$py kman/kmanapp.py看有没有问题
1. 所有路径合并,不用+, 用os.path.join()
1.
```
frozen = 'not'
#CURRPATH = os.path.dirname(os.path.realpath(sys.argv[0]))
CURRPATH = ''
if getattr(sys, 'frozen', False):
# we are running in a bundle
frozen = 'ever so'
CURRPATH = sys._MEIPASS
os.chdir(CURRPATH)
else:
# we are running in a normal Python environment
CURRPATH = os.path.dirname(os.path.abspath(__file__))
```
1. (kmanenv) [gavin@gavin-2 tkman]$pyinstaller --distpath Release -w -c -i kmanapp.ico kmanapp.spec
21750 ERROR: Can not find path ./libshiboken2.abi3.5.15.dylib (needed by /Users/mark/.virtualenvs/kmanenv/lib/python3.7/site-packages/PySide2/QtGui.abi3.so)
A: rm -fr __pycache__, 重新打包
1. (kmanenv) [gavin@gavin-2 kman]$Release/kmanapp.app/Contents/MacOS/kmanapp
NameError: name 'kMan' is not defined
A: pathex=['/Users/mark/penv/kman',]
Analysis(['kmanapp.py'], ==>
Analysis(['kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py', ],
...
**这里有个问题打开app是会运行所有('kman.py', 'kmanapp.py', 'kmanapp_rc.py', 'mainwindow.py', 'mtable.py', 'parseweb.py',)py文件里的main所以在打包时要把不是入口py程序的main删除或注释掉**
# reference
1. [ModuleNotFoundError: No module named 'pkg_resources.py2_warn' ](https://blog.csdn.net/qq_40608730/article/details/104864943)
https://www.cnblogs.com/yemeng/p/6253097.html
https://blog.csdn.net/SCDS_Zyx/article/details/82052396

132
mtable.py Normal file
View File

@@ -0,0 +1,132 @@
#########################################################
## @file : mtable.py
## @desc : pandas is to large,
## so i implement a simple table data structure
## by my self
## @create : 2020/06/25
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
class mTable():
def __init__(self, data=[], index=[], columns=[]):
self._data = data
self._index = index
self._columns = columns
self._width = 10
def dataframe(self, data=[], index=[], columns=[]):
self._data = data
self._index = index
self._columns = columns
def get_iat(self, row: int, column: int):
""" just like pandas iat"""
return self._data[row][column] if len(self._data)>0 else None
def get_num_rows(self):
return len(self._data) if len(self._data)>0 else 0
def get_num_columns(self):
return len(self._columns) if len(self._columns)>0 else 0
def reverse(self):
self._data.reverse()
def set_data(self, data=[]):
""" get table content"""
self._data = data
def get_data(self):
""" get table content"""
return self._data
def get_index(self):
""" just like pandas index"""
return self._index
def get_columns(self):
""" just like pandas columns"""
return self._columns
def set_repr_width(self, width):
self._width = width
def __repr__(self):
""" print table """
w = self._width
s = ''.ljust(w-1,' ')
for c in self._columns: s += c.ljust(w,' ')
s += '\n'
len_index = len(self._index)
for i, r in enumerate(self._data):
s += self._index[i].ljust(w,' ') if i<len_index else str(i).ljust(w,' ')
for c in r:
s += str(c).ljust(w,' ')
s += '\n'
return s
def __next__(self):
# self.a就是当前要迭代的值
result = self.a
# 计算斐波那契数列的下一个值并将a变成原来的b将b变成下一个值
self.a, self.b = self.b, self.a + self.b
# 返回当前迭代的值
return result
def __iter__(self):
return self
if __name__=='__main__':
"""
# move to unitest.kman.py
print('\n---------------------')
data = [['Ohiox','Ohio','Ohio','Nevada','Nevada'],
[2000,2001,2002,2001,2002],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
]
mt = mTable(data,
index = ['one', 'two','three','four','five'],
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[0, 0]\n', mt.get_iat(0, 0))
print('== frame.iat[1, 1]\n', mt.get_iat(1, 1))
print('== frame.iat[2, 2]\n', mt.get_iat(2, 2))
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data,
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data)
mt.set_repr_width(20)
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable()
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
"""
pass

View File

@@ -327,6 +327,8 @@ class bookInfoSpide():
if __name__ == '__main__': if __name__ == '__main__':
"""
# XXX move to unitest.kman.py
spide = bookInfoSpide() spide = bookInfoSpide()
for bkname in testbooks: for bkname in testbooks:
@@ -339,4 +341,6 @@ if __name__ == '__main__':
logger.debug('================ {} ================'.format(bkname)) logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False)) logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False)) logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))
"""
pass

View File

@@ -1,3 +1,2 @@
PySide2==5.15.0 PySide2==5.15.0
pandas==1.0.3
requests==2.23.0 requests==2.23.0

5
searchtitle.md Normal file
View File

@@ -0,0 +1,5 @@
TYPE|BOOKNAME|AUTHOR|MARKTIME|CONTENT
--|--|--|--|--
HL|薛兆丰经济学讲义|薛兆丰|2020/1/13 8:11:05|么到底什么叫边际?边际就是“新增”带来的“新增”。 例如,边际成本就是每新增一个单位产品所需要付出的新增成本;边际收入是每多卖一个产品能够带来的新增收入;边际产量是每新增一份投入所带来的新增产量;边际效用是每消耗一个单位的商品所能带来的新增享受。
HL|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:23:58|一个国家很大,贫富有差距,并非每个学校和家长都能负担得起这样标准的校车。标准太高,就会逼着很多学校,尤其是农村的学校放弃提供校车,家长们就只能使用安全性能更低的交通工具,比如自己骑自行车或雇用黑车等,结果是孩子们享受到的安全保障反而降低了。
NT|薛兆丰经济学讲义|薛兆丰|2020/1/30 10:26:31|山寨 假货 问题

View File

@@ -1,83 +0,0 @@
# CG test pandas
import pandas as pd
from kman import *
data = {
'state':['Ohio','Ohio','Ohio','Nevada','Nevada'],
'year':[2000,2001,2002,2001,2002],
'pop':[1.5,1.7,3.6,2.4,2.9],
'xx':[1.5,1.7,3.6,2.4,2.9]
}
"""
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
"""
frame = pd.DataFrame(data,
index = ['one', 'two','three','four','five'],
columns = ['year','state','pop','debt','xx'])
print(frame.iat[0, 0])
print(frame.iat[1, 1])
print(frame.iat[2, 2])
print(frame.iat[2, 1])
'''
print('\n---------------------')
print(frame)
print('\n---------------------')
frame['debt']=16.5
print(frame)
print('\n---------------------')
print(frame.iloc[2]) #line 2
print('\n---------------------')
print(frame.iloc[[2,3],0:2])
print('\n---------------------')
print(frame.iloc[[2,3],0:2])
print('\n---------------------')
print(frame.iat[1, 1])
print('\n---------------------')
for index, row in frame.iterrows():
print(index)
print(row)
print('\n---------------------')
for row in frame.iterrows():
print(row)
#print(row[0], row[1])
print('\n---------------------')
print(frame.head(2))
print('\n---------------------')
print(frame.columns)
print('\n---------------------')
print(frame.index)
print('\n---------------------')
print(frame.to_dict())
print('\n---------------------')
'''
km = kMan()
books = km.import_clips()
print(books)
print('\n---------------------')
frame = pd.DataFrame(books)
print(frame)
print('\n---------------------')
data = [['Ohio','Ohio','Ohio','Nevada','Nevada'],
[2000,2001,2002,2001,2002],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
]
frame = pd.DataFrame(data,
columns = ['year','state','pop','debt','xx'])
print(frame)

View File

@@ -1,39 +0,0 @@
#########################################################
## @file : tdict.py
## @desc : test dict
## @create : 2020/05/26
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import re
from collections import defaultdict
t = {'a':{'b':{'c':1}}}
t['a']['b']['c']=2
t['a']=2
print(t)
t1 = {'a':{'b':{'c':'3'}}}
t2 = t1
print(t2)
t3 = {'b':{'c':'4'}}
t4 = defaultdict(dict)
t4['a'] = t3
print(t4)
t4['a1']['b2'] = {'c3':'5'}
print(t4)
t4['a1']['d2'] = {'c3':'6'}
print(t4)
t4['a1']['e2'] = {'c3':'7'}
print(t4)
t4['b1']['b2'] = {'b5':1}
print(t4)
t4['b1']['b2']['b3'] = {'b5':1}
print(t4)

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +0,0 @@
# CG test logging
import logging
import os.path
import time
logger = logging.getLogger()
logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.INFO)
logger.setLevel(logging.DEBUG)
tt = 'xxx'
tt1 = 'yyy'
logger.debug('this is a logger debug message {} {}'.format(tt,tt1))
logger.info('this is a logger info message')
logger.warning('this is a logger warning message')
logger.error('this is a logger error message')
logger.critical('this is a logger critical message')

View File

@@ -1,50 +0,0 @@
#########################################################
## @file : tparseamazon.py
## @desc : test parse amazon response
## @create : 2020/05/26
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import re
import json
s =['''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">丹·琼斯(Dan Jones)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">杰弗里·瓦夫罗(Geoffrey Wawro)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">克里斯托弗·希伯特(Christopher Hibbert)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">罗斯·金(Ross King)</span><span class="a-size-base" dir="auto">等等。</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">马克·哈里斯</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">黎绮妮</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">马克·哈里斯</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">黎绮妮</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[美]威廉·厄本(William Urban)</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">陆大鹏</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">刘晓晖</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[英]安德鲁·罗伯茨(Andrew Roberts)</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">苏然</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">[英]安德鲁·罗伯茨(Andrew Roberts)</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">苏然</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">堀田江理(Eri Hotta)</span></div>''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">景跃进</span><span class="a-size-base" dir="auto">, </span><span class="a-size-base" dir="auto">张小劲</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">余逊达</span></div>''']
tre = ['''<div data-asin="B07S46LMCK" data-index="3" data-uuid="ef76dd52-2ab2-4372-8597-2c3258698b6e" data-component-type="s-search-result" class="sg-col-20-of-24 s-result-item s-asin sg-col-0-of-12 sg-col-28-of-32 sg-col-16-of-20 sg-col sg-col-32-of-36 sg-col-12-of-16 sg-col-24-of-28"><div class="sg-col-inner">''',
'''<img src="https://images-cn.ssl-images-amazon.com/images/I/41JFUwuJPCL._AC_UY218_.jpg"''',
'''alt="小窗幽记(国学大书院)(为人处世的智慧之果 修身齐家的行动指南)"''',
'''<div class="a-row a-size-base a-color-secondary"><span class="a-size-base" dir="auto"></span><span class="a-size-base" dir="auto">野田洋次郎</span><span class="a-size-base" dir="auto">、 </span><span class="a-size-base" dir="auto">蒋青青</span></div>''',
'''<span aria-label="4.7 颗星,最多 5 颗星">''',
'''<span class="a-letter-space"></span></div></div>''']
for t in s:
ret = re.split('<span.+?auto\">|<\/span',t)
fret = ret[3::4]
#print(json.dumps(re.split('<span.+?auto\">|<\/span',t), indent=2, ensure_ascii=False))
print(','.join(fret))
re_asin = re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img = re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn = re.compile(r'''^alt=\"(.+?)\"$''')
re_author = re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate = re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end = re.compile(r'''<\/body><\/html>''')
re_end = re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
print(re.search(re_asin, tre[0]).group(1))
print(re.search(re_img , tre[1]).group(1))
print(re.search(re_bn , tre[2]).group(1))
print(re.search(re_author,tre[3]).group(0))
print(re.search(re_rate, tre[4]).group(1))
print(re.search(re_end , tre[5]).group(0))

View File

@@ -1,341 +0,0 @@
#########################################################
## @file : tparsedbcomments.py
## @desc : parse douban comments & reviews
## @create : 2020/06/15
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
#comments - https://book.douban.com/subject/26591910/comments/hot?p=1
# https://book.douban.com/subject/26591910/comments/
#reviews - https://book.douban.com/subject/26591910/reviews/
# https://book.douban.com/subject/26591910/reviews?start=0
import requests
import json
import re
import os
import subprocess
import logging
from collections import defaultdict
# log info
logger=logging.getLogger()
logger.addHandler(logging.FileHandler('log'))
logger.setLevel(logging.DEBUG)
FROMFILE=1
ISDOUBAN=1
IMGPATH = './downimg'
LINKPREF = 'https://book.douban.com/subject/' \
if ISDOUBAN else 'https://www.amazon.cn/s?k='
mheaders = {
'Host': 'www.douban.com',
'Referer': 'http://www.douban.com',
'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
#"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
mparams = {}
murl = ""
if ISDOUBAN==1:
mparams['Host']='www.douban.com',
mparams['search_text'] = 'bkname_xxx'
mparams['cat']='1001'
mparams['k']='bookname_xxx'
murl = "https://search.douban.com/book/subject_search"
else:
mheaders['Host']='www.amazon.cn'
mheaders['Referer']='http:/www.amazon.cn'
#https://www.amazon.cn/s?k={bookname}&i=stripbooks&__mk_zh_CN=亚马逊网站&ref=nb_sb_noss
mparams['Host']='www.amazon.cn'
mparams['k']='bkname_xxx'
mparams['i']='stripbooks'
mparams['__mk_zh_CN=']='亚马逊网站'
mparams['reg']='nb_sb_noss'
murl = 'https://www.amazon.cn/s'
testbooks=['24堂财富课',
'甲骨文',
'庆余年(精校版)',
'商君书-中华经典名著全本全注全译丛书',
'苏世民:我的经验与教训2018读桥水达利欧的原则2020看黑石苏世民的经验!一本书读懂从白手起家到华尔街新国王的传奇人生)',
'杨伯峻_论语译注',
'小窗幽记',
'少年凯歌',
'投资要义',
'白鱼解字',
'历史的巨镜',
'货币的教训',
'钱从哪里来',
'中国古代简史',
'罗马人的故事(套装共15册)',
'改变心理学的40项研究',
'如何假装懂音乐',
'管子(上下册)--中华经典名著全本全注全译(精)',
'投资中最简单的事',
'薛兆丰经济学讲义',
'枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)',
'中央帝国的哲学密码',
'新编说文解字大全集(超值白金版)',
'市场的逻辑(增订本)',
'金融的本质:伯南克四讲美联储(看一个风云人物的金融思考)',
'从零开始学写作:个人增值的有效方法',
'中国国家治理的制度逻辑:一个组织学研究',
'中国为什么有前途对外经济关系的战略潜能第3版',
'日本的世界观(《剑桥日本史》主编凝练之作三个人物故事串起日本两百年变局了解近代日本转向的必读之书理想国出品))']
class bookInfoSpide():
[re_bn,re_bn,re_score,re_star,re_author,re_end]=[None,None,None,None,None,None]
if ISDOUBAN==1:
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\".+?rating_nums\">(.+?)<''', re.S)
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\"''')
re_star=re.compile(r'''^<span class=\"allstar(\d+)\"></span>''')
re_score=re.compile(r'''class=\"rating_nums\">(.+?)<''')
re_ratenum=re.compile(r'''^<span>\((\d+)人评价\)</span>''')
re_author=re.compile(r'''class=\"subject-cast\">(.+?)<''')
else:
re_asin=re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img=re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn=re.compile(r'''^alt=\"(.+?)\"$''')
re_author=re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate=re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end=re.compile(r'''<\/body><\/html>''')
re_end=re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
def __init__(self):
pass
def grab_book_info(self, mbkn: str):
"""mbkn - bookname to be spided
return: {
"25853071": { # sid
"link":"https://....xxxxx"
"bookname": "庆余年",
"img": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2575362797.jpg",
"score": "8.0",
"ratenum": "1000",
"author": "猫腻"
"publisher": "中华书局"
"publishing": "2015"
},...}
"""
bkinfo=defaultdict(dict)
sid=None
stat=None
if FROMFILE:
with open('./tdouban.data', 'r', encoding='utf8', errors='ignore') as f:
resp=f.read()
else:
if ISDOUBAN==1: #douban
mparams['search_text'] = mbkn
else: #amazon
mparams['k'] = mbkn
try:
session = requests.Session()
session.header = mheaders
session.params = mparams
r = session.get( url=murl, headers=mheaders, params=mparams)
#r = requests.get( url=murl, headers=mheaders, params=mparams)
except requests.exceptions.ConnectionError:
print('ConnectionError -- please wait 3 seconds')
time.sleep(3)
except requests.exceptions.ChunkedEncodingError:
print('ChunkedEncodingError -- please wait 3 seconds')
time.sleep(3)
except:
print('Unfortunitely -- An Unknow Error Happened, Please wait 3 seconds')
time.sleep(3)
if r.status_code != 200:
print('grab book {} info from webside failure'.format(mbkn))
if ISDOUBAN==1:
stat='SID'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='SID':
ret=re.search(self.re_bn, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,sid)
bkinfo[sid]['bookname']=ret.group(2)
bkinfo[sid]['img']=ret.group(3)
stat='STAR'
continue
elif stat=='STAR':
ret=re.search(self.re_star, line)
if ret:
star = ret.group(1)
if star=='00':
stat='AUTHOR'
elif int(star) > 0:
stat='SCORE'
elif stat=='SCORE':
ret=re.search(self.re_score, line)
if ret:
bkinfo[sid]['score']=ret.group(1)
stat='RATENUM'
continue
elif stat=='RATENUM':
ret=re.search(self.re_ratenum, line)
if ret:
bkinfo[sid]['ratenum']=ret.group(1)
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
tt=ret.group(1).split(' / ')
if len(tt)>=3:
*author, bkinfo[sid]['publisher'], bkinfo[sid]['publishing']=tt
bkinfo[sid]['author']='/'.join(author)
else:
bkinfo[sid]['author']=ret[0]
stat='SID'
else: continue
else:
stat='ASIN'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='ASIN':
ret=re.search(self.re_asin, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,ret.group(1))
stat='IMG'
continue
elif stat=='IMG':
ret=re.search(self.re_img, line)
if ret:
bkinfo[sid]['img']=ret.group(1)
stat='BOOKNAME'
continue
elif stat=='BOOKNAME':
ret=re.search(self.re_bn, line)
if ret:
bkname=re.split(r'[(\s]',ret.group(1).strip())[0]
bkinfo[sid]['bookname']=bkname
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
author=','.join(re.split('<span.+?auto\">|<\/span', ret.group(0))[3::4])
bkinfo[sid]['author']=author
stat='RATE'
continue
elif stat=='RATE':
ret=re.search(self.re_rate, line)
if ret:
bkinfo[sid]['rate']=ret.group(1).split(' ')[0]
stat='AUTHOR'
continue
else: continue
if re.search(self.re_end, line):
stat=='ASIN'
continue
return [mbkn, bkinfo]
def filter_spide_book(self, mbkinfo):
"""
mbkinfo:
douban
"10530219": {
"link": "https://book.douban.com/subject/10530219",
"bookname": "市场的逻辑",
"img": "https://img3.doubanio.com/view/subject/s/public/s8912552.jpg",
"score": "8.3",
"ratenum": "218",
"publisher": "世纪文景 上海人民出版社",
"publishing": "2012",
"author": "张维迎"
},...}
amazon
"孟子": {
"link": "https://....B07RN73425",
"bookname": "古典名著普及文库:孟子",
"img": "https://images-cn.ssl-images-amazon.com/images/I/511vbVrhIBL._AC_UY218_.jpg",
"rate": "3.9"
"author": "孙钦善",
}
"""
#booklink - https://book.douban.com/subject/{sid}
# f1/d1: mbkn include in bookname
# f2/d2: bookname include mbkn
# f3/d3: mbkn and bookname different
[f1,f2,f3]=[0,0,0]
[d1,d2,d3] =[{},{},{}]
mbkn=mbkinfo[0]
for k,v in mbkinfo[1].items():
bkn=v['bookname']
if len(v)==8:
if (not f1) and (mbkn in bkn):
f1=1
d1={mbkn:v}
elif (not f1) and (not f2) and (bkn in mbkn):
f2=1
d2={mbkn:v}
elif (not f3):
f3=1
d3={mbkn:v}
else: continue
else:
continue
if f1:
return d1
elif f2:
return d2
elif f3:
return d3
return None
def down_book_img(self, mbkinfo):
import os
import socket
from urllib.request import urlretrieve
headers={'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
for k,v in mbkinfo.items():
link=v['img']
if not os.path.exists(IMGPATH): os.mkdir(IMGPATH)
p=os.path.join(IMGPATH,link.split('/')[-1])
try:
img=requests.get(link, headers=headers)
if img.status_code==200:
with open(p, 'wb') as fp:
fp.write(img.content)
except Exception as e:
print(e)
if __name__=='__main__':
spide=bookInfoSpide()
for bkname in testbooks:
bkname=re.split(r'[\(\-\:_\s]',bkname.strip())[0]
bkinfo=spide.grab_book_info(bkname)
filter_bkinfo=spide.filter_spide_book(bkinfo)
if filter_bkinfo: spide.down_book_img(filter_bkinfo)
#logger.debug('================ {} ================'.format(bkname))
#logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))

View File

@@ -1,345 +0,0 @@
#########################################################
## @file : tparsedbinfo.py
## @desc : kindle note managerment tool
## @create : 2020/06/15
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import requests
import json
import re
import os
import subprocess
import logging
from collections import defaultdict
# log info
logger=logging.getLogger()
logger.addHandler(logging.FileHandler('log'))
logger.setLevel(logging.DEBUG)
FROMFILE=1
ISDOUBAN=1
IMGPATH = './downimg'
LINKPREF = 'https://book.douban.com/subject/' \
if ISDOUBAN else 'https://www.amazon.cn/s?k='
mheaders = {
'Host': 'www.douban.com',
'Referer': 'http://www.douban.com',
'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
}
#"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36"
mparams = {}
murl = ""
if ISDOUBAN==1:
mparams['Host']='www.douban.com',
mparams['search_text'] = 'bkname_xxx'
mparams['cat']='1001'
mparams['k']='bookname_xxx'
murl = "https://search.douban.com/book/subject_search"
else:
mheaders['Host']='www.amazon.cn'
mheaders['Referer']='http:/www.amazon.cn'
#https://www.amazon.cn/s?k={bookname}&i=stripbooks&__mk_zh_CN=亚马逊网站&ref=nb_sb_noss
mparams['Host']='www.amazon.cn'
mparams['k']='bkname_xxx'
mparams['i']='stripbooks'
mparams['__mk_zh_CN=']='亚马逊网站'
mparams['reg']='nb_sb_noss'
murl = 'https://www.amazon.cn/s'
testbooks=['24堂财富课',
'甲骨文',
'庆余年(精校版)',
'商君书-中华经典名著全本全注全译丛书',
'苏世民:我的经验与教训2018读桥水达利欧的原则2020看黑石苏世民的经验!一本书读懂从白手起家到华尔街新国王的传奇人生)',
'杨伯峻_论语译注',
'小窗幽记',
'少年凯歌',
'投资要义',
'白鱼解字',
'历史的巨镜',
'货币的教训',
'钱从哪里来',
'中国古代简史',
'罗马人的故事(套装共15册)',
'改变心理学的40项研究',
'如何假装懂音乐',
'管子(上下册)--中华经典名著全本全注全译(精)',
'投资中最简单的事',
'薛兆丰经济学讲义',
'枪炮、病菌与钢铁:人类社会的命运(世纪人文系列丛书·开放人文)',
'中央帝国的哲学密码',
'新编说文解字大全集(超值白金版)',
'市场的逻辑(增订本)',
'金融的本质:伯南克四讲美联储(看一个风云人物的金融思考)',
'从零开始学写作:个人增值的有效方法',
'中国国家治理的制度逻辑:一个组织学研究',
'中国为什么有前途对外经济关系的战略潜能第3版',
'日本的世界观(《剑桥日本史》主编凝练之作三个人物故事串起日本两百年变局了解近代日本转向的必读之书理想国出品))']
class bookInfoSpide():
[re_bn,re_bn,re_score,re_star,re_author,re_description,re_end]=[None,None,None,None,None,None,None]
if ISDOUBAN==1:
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\".+?rating_nums\">(.+?)<''', re.S)
re_bn=re.compile(r'''class=\"nbg.+?sid: (\d+?),.+?title=\"(.+?)\".+?img src=\"(.+?)\"''')
re_star=re.compile(r'''^<span class=\"allstar(\d+)\"></span>''')
re_score=re.compile(r'''class=\"rating_nums\">(.+?)<''')
re_ratenum=re.compile(r'''^<span>\((\d+)人评价\)</span>''')
re_author=re.compile(r'''class=\"subject-cast\">(.+?)<''')
re_description=re.compile(r'''^<p>(.+?)(<\/p>){0,1}$''')
else:
re_asin=re.compile(r'''^<div data-asin=\"(.+?)\" data-index''')
re_img=re.compile(r'''^<img src=\"(.+?)\"$''')
re_bn=re.compile(r'''^alt=\"(.+?)\"$''')
re_author=re.compile(r'''^<div class=.+auto\"><\/span>.+$''')
re_rate=re.compile(r'''^<span aria-label=\"(.+?)\">$''')
#re_end=re.compile(r'''<\/body><\/html>''')
re_end=re.compile(r'''^<span class=\"a-letter-space\"><\/span><\/div><\/div>''')
def __init__(self):
pass
def grab_book_info(self, mbkn: str):
"""mbkn - bookname to be spided
return: {
"25853071": { # sid
"link":"https://....xxxxx"
"bookname": "庆余年",
"img": "https://img1.doubanio.com/view/photo/s_ratio_poster/public/p2575362797.jpg",
"score": "8.0",
"ratenum": "1000",
"author": "猫腻"
"publisher": "中华书局"
"publishing": "2015"
},...}
"""
bkinfo=defaultdict(dict)
sid=None
stat=None
if FROMFILE:
with open('./tdouban.data', 'r', encoding='utf8', errors='ignore') as f:
resp=f.read()
else:
if ISDOUBAN==1: #douban
mparams['search_text'] = mbkn
else: #amazon
mparams['k'] = mbkn
try:
session = requests.Session()
session.header = mheaders
session.params = mparams
r = session.get( url=murl, headers=mheaders, params=mparams)
#r = requests.get( url=murl, headers=mheaders, params=mparams)
except requests.exceptions.ConnectionError:
print('ConnectionError -- please wait 3 seconds')
time.sleep(3)
except requests.exceptions.ChunkedEncodingError:
print('ChunkedEncodingError -- please wait 3 seconds')
time.sleep(3)
except:
print('Unfortunitely -- An Unknow Error Happened, Please wait 3 seconds')
time.sleep(3)
if r.status_code != 200:
print('grab book {} info from webside failure'.format(mbkn))
if ISDOUBAN==1:
stat='SID'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='SID':
ret=re.search(self.re_bn, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,sid)
bkinfo[sid]['bookname']=ret.group(2)
bkinfo[sid]['img']=ret.group(3)
stat='STAR'
continue
elif stat=='STAR':
ret=re.search(self.re_star, line)
if ret:
star = ret.group(1)
if star=='00':
stat='AUTHOR'
elif int(star) > 0:
stat='SCORE'
elif stat=='SCORE':
ret=re.search(self.re_score, line)
if ret:
bkinfo[sid]['score']=ret.group(1)
stat='RATENUM'
continue
elif stat=='RATENUM':
ret=re.search(self.re_ratenum, line)
if ret:
bkinfo[sid]['ratenum']=ret.group(1)
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
tt=ret.group(1).split(' / ')
if len(tt)>=3:
*author, bkinfo[sid]['publisher'], bkinfo[sid]['publishing']=tt
bkinfo[sid]['author']='/'.join(author)
else:
bkinfo[sid]['author']=ret[0]
stat='DESCRIPTION'
continue
elif stat=='DESCRIPTION':
ret=re.search(self.re_description, line)
if ret:
bkinfo[sid]['description']=ret.group(1)
stat='SID'
continue
else: continue
else:
stat='ASIN'
for line in resp.split('\n'):
line=line.strip()
if line=='': continue
if stat=='ASIN':
ret=re.search(self.re_asin, line)
if ret:
sid=ret.group(1)
bkinfo[sid]['link']=os.path.join(LINKPREF,ret.group(1))
stat='IMG'
continue
elif stat=='IMG':
ret=re.search(self.re_img, line)
if ret:
bkinfo[sid]['img']=ret.group(1)
stat='BOOKNAME'
continue
elif stat=='BOOKNAME':
ret=re.search(self.re_bn, line)
if ret:
bkname=re.split(r'[(\s]',ret.group(1).strip())[0]
bkinfo[sid]['bookname']=bkname
stat='AUTHOR'
continue
elif stat=='AUTHOR':
ret=re.search(self.re_author, line)
if ret:
author=','.join(re.split('<span.+?auto\">|<\/span', ret.group(0))[3::4])
bkinfo[sid]['author']=author
stat='RATE'
continue
elif stat=='RATE':
ret=re.search(self.re_rate, line)
if ret:
bkinfo[sid]['rate']=ret.group(1).split(' ')[0]
stat='AUTHOR'
continue
else: continue
if re.search(self.re_end, line):
stat=='ASIN'
continue
return [mbkn, bkinfo]
def filter_spide_book(self, mbkinfo):
"""
mbkinfo:
douban
"10530219": {
"link": "https://book.douban.com/subject/10530219",
"bookname": "市场的逻辑",
"img": "https://img3.doubanio.com/view/subject/s/public/s8912552.jpg",
"score": "8.3",
"ratenum": "218",
"publisher": "世纪文景 上海人民出版社",
"publishing": "2012",
"author": "张维迎"
},...}
amazon
"孟子": {
"link": "https://....B07RN73425",
"bookname": "古典名著普及文库:孟子",
"img": "https://images-cn.ssl-images-amazon.com/images/I/511vbVrhIBL._AC_UY218_.jpg",
"rate": "3.9"
"author": "孙钦善",
}
"""
#booklink - https://book.douban.com/subject/{sid}
# f1/d1: mbkn include in bookname
# f2/d2: bookname include mbkn
# f3/d3: mbkn and bookname different
[f1,f2,f3]=[0,0,0]
[d1,d2,d3] =[{},{},{}]
mbkn=mbkinfo[0]
for k,v in mbkinfo[1].items():
bkn=v['bookname']
if len(v)==9:
if (not f1) and (mbkn in bkn):
f1=1
d1={mbkn:v}
elif (not f1) and (not f2) and (bkn in mbkn):
f2=1
d2={mbkn:v}
elif (not f3):
f3=1
d3={mbkn:v}
else: continue
else:
continue
if f1:
return d1
elif f2:
return d2
elif f3:
return d3
return None
def down_book_img(self, mbkinfo):
import os
import socket
from urllib.request import urlretrieve
headers={'User-Agent': 'User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'}
for k,v in mbkinfo.items():
link=v['img']
if not os.path.exists(IMGPATH): os.mkdir(IMGPATH)
p=os.path.join(IMGPATH,link.split('/')[-1])
try:
img=requests.get(link, headers=headers)
if img.status_code==200:
with open(p, 'wb') as fp:
fp.write(img.content)
except Exception as e:
print(e)
if __name__=='__main__':
spide=bookInfoSpide()
for bkname in testbooks:
bkname=re.split(r'[\(\-\:_\s]',bkname.strip())[0]
bkinfo=spide.grab_book_info(bkname)
filter_bkinfo=spide.filter_spide_book(bkinfo)
if filter_bkinfo: spide.down_book_img(filter_bkinfo)
#logger.debug('================ {} ================'.format(bkname))
#logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))

View File

@@ -1,88 +0,0 @@
# -*- coding: utf-8 -*-
"""
@author: Taar
"""
#conversion of https://github.com/openwebos/qt/tree/master/examples/tutorials/modelview/5_edit
#I work with ints for values instead of strings (less cumbersome to add)
import sys
import pandas as pd
from PySide2.QtWidgets import *
from PySide2.QtGui import (QBrush, QColor, QConicalGradient, QCursor, QFont,
QFontDatabase, QIcon, QKeySequence, QLinearGradient, QPalette, QPainter,
QPixmap, QRadialGradient, QStandardItem, QStandardItemModel)
from PySide2.QtCore import (QCoreApplication, QDate, QDateTime, QMetaObject,
QAbstractTableModel, QObject, QPoint, QRect, QSize, QTime,
QUrl, Qt, QThread, Signal, QTimer)
class TableModel(QAbstractTableModel):
def __init__(self, data):
super(TableModel, self).__init__()
self._data = data
def data(self, index, role):
if role == Qt.DisplayRole:
value = self._data.iloc[index.row(), index.column()]
return str(value)
def rowCount(self, index):
return self._data.shape[0]
def columnCount(self, index):
return self._data.shape[1]
def headerData(self, section, orientation, role):
# section is the index of the column/row.
if role == Qt.DisplayRole:
if orientation == Qt.Horizontal:
return str(self._data.columns[section])
if orientation == Qt.Vertical:
return str(self._data.index[section])
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = QTableView()
data = pd.DataFrame([
[1, 9, 2],
[1, 0, -1],
[3, 5, 2],
[3, 3, 2],
[5, 8, 9],
],
columns = ['A', 'B', 'C'],
index=['Row 1', 'Row 2', 'Row 3', 'Row 4', 'Row 5'])
self.model = TableModel(data)
self.table.setModel(self.model)
data = pd.DataFrame([
[1, 5, 5, 9],
[1, 5, 5, 9],
[1, 2, 1, 3],
[1, 1, 0, -1],
[1, 5, 5, 2],
[1, 5, 5, 8],
[1, 5, 5, 9],
],
columns = ['Aa', 'Bb', 'Cc', 'dd'],
index=['Row 1', 'Row 3', 'Row 4', 'Row 4', 'Row 5', '6', '7'])
self.model = TableModel(data)
self.table.setModel(self.model)
self.setCentralWidget(self.table)
if __name__ == '__main__':
app=QApplication(sys.argv)
window=MainWindow()
window.show()
app.exec_()

View File

@@ -1,37 +0,0 @@
#########################################################
## @file : tsqlite.py
## @desc : test sqlite3
## @create : 2020/06/11
## @author : Chengan
## @email : douboer@gmail.com
#########################################################
import sqlite3
dbname = 'vocab.db'
conn = sqlite3.connect(dbname)
try:
cursor = conn.cursor()
sql1 = '''select * from words;'''
sql2 = '''select * from lookups;'''
# fetch one record
cursor.execute(sql2)
one=cursor.fetchone()
print(one)
# fetch all records
for row in cursor.fetchall():
print(row)
# fetch table structure
except Exception as e:
print('sql failure {}'.format(e))
pass
finally:
conn.close()

View File

@@ -1,6 +0,0 @@
import platform
print(platform.system(),platform.machine(),platform.platform(aliased=0, terse=0) )

View File

@@ -1,106 +0,0 @@
import sys
import pyqtgraph as pg
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QThread, pyqtSignal, QTimer
import numpy as np
class PlotSin(QThread):
# 定义信号self.y 是 numpy.array所以信号数据类型为 object
signal = pyqtSignal(object)
def __init__(self, parent=None):
super().__init__()
self.y = None
self.phase = 0
def sin(self):
self.x = np.arange(0, 3.0, 0.01)
self.y = np.sin(2 * np.pi * self.x + self.phase)
self.phase += 0.1
QThread.msleep(200) # 等待200毫秒
def run(self):
for _ in range(300):
self.sin()
self.signal.emit(self.y) # 向连接槽发射信号 self.y
class PlotSin_MainWindow(QDialog):
def __init__(self):
super().__init__()
self.initUI()
self.clock_time = 0
self.timer = QTimer(self) # 生成定时器
self.timer.timeout.connect(self.clock) # 绑定计时函数 self.clock
def initUI(self):
self.creatContorls("时间显示:")
self.creatResult("函数绘制:")
layout = QHBoxLayout()
layout.addWidget(self.controlsGroup)
layout.addWidget(self.resultGroup)
self.setLayout(layout)
self.beginButton.clicked.connect(self.clock_begin)
self.setGeometry(300, 300, 600, 300)
self.setWindowTitle('Plot Sine')
self.show()
def creatContorls(self,title):
self.controlsGroup = QGroupBox(title)
self.beginButton = QPushButton("开始")
numberLabel = QLabel("运行时间:")
self.clockLabel = QLabel("")
controlsLayout = QGridLayout()
controlsLayout.addWidget(numberLabel, 0, 0)
controlsLayout.addWidget(self.clockLabel, 0, 1)
controlsLayout.addWidget(self.beginButton, 3, 0)
self.controlsGroup.setLayout(controlsLayout)
def creatResult(self,title):
self.resultGroup = QGroupBox(title)
self.guiplot = pg.PlotWidget()
gridLayout = QGridLayout()
gridLayout.addWidget(self.guiplot,0,2,2,3)
self.resultGroup.setLayout(gridLayout)
def clock_begin(self):
if not self.timer.isActive():
self.recorder_thread = PlotSin()
self.recorder_thread.signal.connect(self.displaySin) # 绑定信号槽函数
self.recorder_thread.start() # 线程执行
self.clock()
self.timer.start(1000)
else:
self.beginButton.setText("开始")
self.clockLabel.setText("")
self.recorder_thread.terminate() # 终止线程
self.timer.stop() # 终止定时器
self.clock_time = 0
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
def clock(self):
text = str(self.clock_time) + "s"
self.clockLabel.setText(text)
if self.clock_time == 0:
self.beginButton.setText("结束")
self.clock_time += 1
def plotSin(self, y):
self.guiplot.clear()
self.guiplot.plot(y)
def displaySin(self, y):
self.plotSin(y)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = PlotSin_MainWindow()
window.show()
sys.exit(app.exec_())

View File

@@ -1,88 +0,0 @@
# from webside not test, only for reference XXX
#coding=utf8
import random
import requests
import hashlib
appid = 'xxxxxx'
secretKey = 'xxxxx'
def get_md5(string):#返回字符串md5加密
hl = hashlib.md5()
hl.update(string.encode('utf-8'))
return hl.hexdigest()
def en_to_zh(en_str):#英语翻译成中文
api_url = 'http://api.fanyi.baidu.com/api/trans/vip/translate'
salt = random.randint(32768,65536)
sign = get_md5(appid + en_str + str(salt) + secretKey)
api_data = {
'q':en_str,
'from':'en',
'to':'zh',
'appid':appid,
'salt':salt,
'sign':sign
}
req_get = requests.get(api_url,api_data)
result = req_get.json()
print(result)
return result['trans_result']
print(en_to_zh('test'))
'''
import urllib.request
import urllib.parse
import json
from tkinter import *
root=Tk()
root.title("翻译小程序")
sw = root.winfo_screenwidth()
#得到屏幕宽度
sh = root.winfo_screenheight()
#得到屏幕高度
ww = 500
wh = 300
x = (sw-ww) / 2
y = (sh-wh) / 2-50
root.geometry("%dx%d+%d+%d" %(ww,wh,x,y))
lb2=Label(root,text="输入英文翻译中文,或者输入中文翻译英文,按回车键翻译。--版权所有,翻录必究。")
lb2.place(relx=0, rely=0.05)
txt = Text(root,font=("宋体",20))
txt.place(rely=0.6, relheight=0.4,relwidth=1)
inp1 = Entry(root,font=("",20))
inp1.place(relx=0, rely=0.2, relwidth=1, relheight=0.25)
def run2(event):
txt.delete("0.0",END)
a = (inp1.get())
content = a
url='http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule'
data={}
data['i'] = content
data['from'] = 'AUTO'
data['to'] = 'AUTO'
data['smartresult'] = 'dict'
data['client'] = 'fanyideskweb'
data['salt'] = '15812376682056'
data['sign'] = 'a1246b257926af8432be022564ff79f5'
data['ts'] = '1581237668205'
data['bv'] = '656f750600466990f874a839d9f5ad23'
data['doctype'] = 'json'
data['version'] = '2.1'
data['keyfrom'] = 'fanyi.web'
data['action'] = 'FY_BY_CLICKBUTTION'
data = urllib.parse.urlencode(data).encode('utf-8')
response = urllib.request.urlopen(url,data)
html = response.read().decode('utf-8')
target = json.loads(html)
s=("%s"%(target['translateResult'][0][0]['tgt'])+"\n")
print(s)
txt.insert(END, s)
def button1(event):
btn1 = Button(root, text='结果如下', font=("",12))
btn1.place(relx=0.35, rely=0.45, relwidth=0.2, relheight=0.15)
inp1.bind("<Return>",run2)
button1(1)
root.mainloop()
'''

View File

@@ -13,6 +13,8 @@ import json
from collections import defaultdict from collections import defaultdict
from kman import * from kman import *
from parseweb import *
from mtable import *
# log info # log info
logger = logging.getLogger() logger = logging.getLogger()
@@ -31,6 +33,7 @@ class TestKman(unittest.TestCase):
DELIMITER= '|' DELIMITER= '|'
self.km = kMan() self.km = kMan()
self.util = Util()
global t_bm_sec global t_bm_sec
global t_hl_sec global t_hl_sec
@@ -166,7 +169,7 @@ class TestKman(unittest.TestCase):
print("与预期匹配sidx 3 重复被删除,抛出: %s" % 'keyerror') print("与预期匹配sidx 3 重复被删除,抛出: %s" % 'keyerror')
t_secd.clear() t_secd.clear()
# test function format_time() # test function format_time()
def test_format_time(self): def test_format_time(self):
t_ds = '2020年1月13日 星期一 下午 8:11:05' t_ds = '2020年1月13日 星期一 下午 8:11:05'
@@ -216,7 +219,7 @@ class TestKman(unittest.TestCase):
logger.debug('========== 2 ==========\n') logger.debug('========== 2 ==========\n')
logger.debug(json.dumps(bn, indent=2, ensure_ascii=False)) logger.debug(json.dumps(bn, indent=2, ensure_ascii=False))
bn = {} bn = {}
# by author # by author
bn = self.km.filter_clips(t_books, '贾雷德·戴蒙德', 2) bn = self.km.filter_clips(t_books, '贾雷德·戴蒙德', 2)
logger.debug('========== 3 ==========\n') logger.debug('========== 3 ==========\n')
@@ -227,10 +230,61 @@ class TestKman(unittest.TestCase):
def test_import_words(self): def test_import_words(self):
import pprint import pprint
[bookinfo, lookups, words] = self.km.import_words() [bookinfo, lookups, words] = self.km.import_words()
# how to beauty print to logger file # how to beauty print to logger file
logger.debug('========== 4 ==========\n') logger.debug('========== 4 ==========\n')
logger.debug(json.dumps(bookinfo, indent=2, ensure_ascii=False)) logger.debug(json.dumps(bookinfo, indent=2, ensure_ascii=False))
logger.debug('========== 5 ==========\n') logger.debug('========== 5 ==========\n')
logger.debug(json.dumps(lookups, indent=2, ensure_ascii=False))
logger.debug('========== 6 ==========\n')
logger.debug(json.dumps(words, indent=2, ensure_ascii=False))
# test filter_words()
logger.debug('========== 7 ==========\n')
self.km.filter_words(self.km.import_words(), '中国历史风云录 (陈舜臣作品)', tp=0)
logger.debug('========== 8 ==========\n')
self.km.filter_words(self.km.import_words(), 'zh:闾', tp=1)
def test_util(self):
logger.debug( 'test get_app_path: {}'.format( self.util.get_app_path()))
def test_mtable(self):
print('\n---------------------')
data = [['Ohiox','Ohio','Ohio','Nevada','Nevada'],
[2000,2001,2002,2001,2002],
[1.5,1.7,3.6,2.4,2.9],
[1.5,1.7,3.6,2.4,2.9],
]
mt = mTable(data,
index = ['one', 'two','three','four','five'],
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[0, 0]\n', mt.get_iat(0, 0))
print('== frame.iat[1, 1]\n', mt.get_iat(1, 1))
print('== frame.iat[2, 2]\n', mt.get_iat(2, 2))
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data,
columns = ['year','state','pop','debt','xx'])
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index())
mt = mTable(data)
mt.set_repr_width(20)
print('== frame\n', mt)
print('== frame.iat[2, 1]\n', mt.get_iat(2, 1))
print('== frame.shape[0]\n', mt.get_num_rows())
print('== frame.shape[1]\n', mt.get_num_columns())
print('== frame.columns\n', mt.get_columns()) print('== frame.columns\n', mt.get_columns())
print('== frame.index\n', mt.get_index()) print('== frame.index\n', mt.get_index())
@@ -245,6 +299,80 @@ class TestKman(unittest.TestCase):
""" """
def test_search_clip(self): def test_search_clip(self):
pass pass
def test_statistic(self):
pass
def test_dict2json(self):
pass
def test_json2dict(self):
pass
def test_import_clips(self):
pass
"""
def test_parseweb(self):
spide = bookInfoSpide()
"""
for bkname in testbooks:
bkname = re.split(r'[\(\-\:_\s]',bkname.strip())[0]
print(bkname)
bkinfo = spide.grab_book_info(bkname)
filter_bkinfo = spide.filter_spide_book(bkinfo)
if filter_bkinfo: spide.down_book_img(filter_bkinfo)
logger.debug('================ {} ================'.format(bkname))
logger.debug(json.dumps(bkinfo,indent=2, ensure_ascii=False))
logger.debug(json.dumps(filter_bkinfo,indent=2, ensure_ascii=False))
"""
def test_kman(self):
#books = defaultdict(dict)
km = kMan()
books = km.import_clips()
# remove duplication
km.drop_duplicate(books)
# test search note function
searchnote = km.search_clip(books, '三大都市圈', 'ALL', 'CONTENT')
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchcontent', ft='MD')
searchnote = km.search_clip(books, '经济', 'ALL', 'TITLE')
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchtitle', ft='MD')
searchnote = km.search_clip(books, '巴曙松', 'ALL', 'AUTHOR')
if searchnote[0] > 0: km.export_notes(searchnote[1], 'searchauthor', ft='MD')
#print(km.get_bookname_num(books))
#print(km.get_author_num(books))
# add note content to hightlight, then delete note
km.add_note_to_highlight(books)
# test dict json convert
with open(os.path.join(CURRPATH,'xx'), 'w', encoding='utf8', errors='ignore') as fw:
fw.write(km.dict2json(books))
if km.json2dict(os.path.join(CURRPATH,'xx'))==books: print( 'test OK')
km.export_notes(books, OUTPREF, ft='MD')
# print data with json format
logger.debug(json.dumps(books, indent=4, sort_keys=True, ensure_ascii=False))
def test_somepath(self):
frozen = 'not'
if getattr(sys, 'frozen', False):
# we are running in a bundle
frozen = 'ever so'
bundle_dir = sys._MEIPASS
else:
# we are running in a normal Python environment
bundle_dir = os.path.dirname(os.path.abspath(__file__))
print( 'we are',frozen,'frozen')
print( 'bundle dir is', bundle_dir )
print( 'sys.argv[0] is', sys.argv[0] )
print( 'sys.executable is', sys.executable )
print( 'os.getcwd is', os.getcwd() )
print('sys.path[0]', sys.path[0])
print('sys.argv[0]', sys.argv[0]) print('sys.argv[0]', sys.argv[0])
print('os.path.realpath(sys.executable)', os.path.realpath(sys.executable)) print('os.path.realpath(sys.executable)', os.path.realpath(sys.executable))
print('os.path.realpath(sys.argv[0]))', os.path.realpath(sys.argv[0])) print('os.path.realpath(sys.argv[0]))', os.path.realpath(sys.argv[0]))
@@ -265,4 +393,3 @@ if __name__ == '__main__':
suite = unittest.TestSuite () suite = unittest.TestSuite ()
suite.addTest(TestKman('test_parse_section')) suite.addTest(TestKman('test_parse_section'))
suite.addTest(TestKman('test_format_time')) suite.addTest(TestKman('test_format_time'))
suite.addTest(TestKman('test_format_data'))

1956
x

File diff suppressed because it is too large Load Diff

1
xx Normal file
View File

@@ -0,0 +1 @@
{"\u859b\u5146\u4e30\u7ecf\u6d4e\u5b66\u8bb2\u4e49": {"author": "\u859b\u5146\u4e30", "1": {"type": "HL", "position": "1408-1410", "day": "2020\u5e741\u670813\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0a\u5348", "time": "8:11:05", "content": "\u4e48\u5230\u5e95\u4ec0\u4e48\u53eb\u8fb9\u9645\uff1f\u8fb9\u9645\u5c31\u662f\u201c\u65b0\u589e\u201d\u5e26\u6765\u7684\u201c\u65b0\u589e\u201d\u3002 \u4f8b\u5982\uff0c\u8fb9\u9645\u6210\u672c\u5c31\u662f\u6bcf\u65b0\u589e\u4e00\u4e2a\u5355\u4f4d\u4ea7\u54c1\u6240\u9700\u8981\u4ed8\u51fa\u7684\u65b0\u589e\u6210\u672c\uff1b\u8fb9\u9645\u6536\u5165\u662f\u6bcf\u591a\u5356\u4e00\u4e2a\u4ea7\u54c1\u80fd\u591f\u5e26\u6765\u7684\u65b0\u589e\u6536\u5165\uff1b\u8fb9\u9645\u4ea7\u91cf\u662f\u6bcf\u65b0\u589e\u4e00\u4efd\u6295\u5165\u6240\u5e26\u6765\u7684\u65b0\u589e\u4ea7\u91cf\uff1b\u8fb9\u9645\u6548\u7528\u662f\u6bcf\u6d88\u8017\u4e00\u4e2a\u5355\u4f4d\u7684\u5546\u54c1\u6240\u80fd\u5e26\u6765\u7684\u65b0\u589e\u4eab\u53d7\u3002"}, "2": {"type": "HL", "position": "4284-4286", "day": "2020\u5e741\u670830\u65e5", "week": "\u661f\u671f\u56db", "meridiem": "\u4e0a\u5348", "time": "10:23:58", "content": "\u4e00\u4e2a\u56fd\u5bb6\u5f88\u5927\uff0c\u8d2b\u5bcc\u6709\u5dee\u8ddd\uff0c\u5e76\u975e\u6bcf\u4e2a\u5b66\u6821\u548c\u5bb6\u957f\u90fd\u80fd\u8d1f\u62c5\u5f97\u8d77\u8fd9\u6837\u6807\u51c6\u7684\u6821\u8f66\u3002\u6807\u51c6\u592a\u9ad8\uff0c\u5c31\u4f1a\u903c\u7740\u5f88\u591a\u5b66\u6821\uff0c\u5c24\u5176\u662f\u519c\u6751\u7684\u5b66\u6821\u653e\u5f03\u63d0\u4f9b\u6821\u8f66\uff0c\u5bb6\u957f\u4eec\u5c31\u53ea\u80fd\u4f7f\u7528\u5b89\u5168\u6027\u80fd\u66f4\u4f4e\u7684\u4ea4\u901a\u5de5\u5177\uff0c\u6bd4\u5982\u81ea\u5df1\u9a91\u81ea\u884c\u8f66\u6216\u96c7\u7528\u9ed1\u8f66\u7b49\uff0c\u7ed3\u679c\u662f\u5b69\u5b50\u4eec\u4eab\u53d7\u5230\u7684\u5b89\u5168\u4fdd\u969c\u53cd\u800c\u964d\u4f4e\u4e86\u3002--CG\u6ce8:\u5c71\u5be8 \u5047\u8d27 \u95ee\u9898"}}, "\u5e86\u4f59\u5e74(\u7cbe\u6821\u7248\uff09": {"author": "\u732b\u817b", "4": {"type": "HL", "position": "48484-48484", "day": "2020\u5e741\u670819\u65e5", "week": "\u661f\u671f\u65e5", "meridiem": "\u4e0b\u5348", "time": "8:00:29", "content": "\u56ed\u5b50\u91cc\u7684\u62a4\u536b\u80fd\u63ba\u591a\u5c11\u4eba\u5c31\u63ba\u591a\u5c11\u4eba\uff0c\u6211\u4f1a\u6d3e\u4eba\u76ef\u7740"}, "5": {"type": "HL", "position": "49901-49901", "day": "2020\u5e741\u670820\u65e5", "week": "\u661f\u671f\u4e00", "meridiem": "\u4e0b\u5348", "time": "7:57:10", "content": "\u53f6\u7075\u513f\u53f9\u4e86\u53e3"}}}