用程序生成word文档(DOC)

很多程序都支持导出PDF文档,不过如果需要对导出文档进行编辑PDF就显得不那么方便了。就国内环境而言,导出word文档对有编辑需求的文档而言更为合适。
由于我使用python,因此这里只讨论python下可用的方案。目前找到的解决方案主要有下面几种

  • python-docx
    目前能找到支持word格式的库非常的少,就python而言只找到 python-docx 算是相对可用的解决方案。python-docx 的功能非常的弱,有很多的限制,比如不支持模板等。如果想生成复杂文档那就无能为力了。
  • POD
    POD是Appy框架的一部分,可使用ODF (Open Document Format)文件做为模板,并输出ODF格式的文件,并可调用LibreOffice将生成的文件转换成DOC等格式。相比python-docx,POD用于生成更为复杂的文档。但如果你需要动态生成一些复杂表格,POD可能会有些问题。
  • unoconv
    unoconv是个文档转换工具,可调用LibreOffice对文档格式进行转换。unoconv可将HTML转换为DOC格式。因此可先生成HTML,然后再将HTML转换为DOC。生成HTML对广大的WEB开发者而言无疑是轻而易举的,这也是我最终选择的方案。不过要注意的是用unoconv将HTML转换成DOC,遍地是坑-_-!。

    • 只支持有限的HTML语法。很多CSS语法根本就不支持,看到的和转换出来的效果完全不一样。解决办法:在LibreOffice中编辑文档,然后保存成HTML,然后对保存的HTML进行编辑。
    • unoconv的-t参数可传入.ott格式的文档模板。默认情况下LibreOffice转换出的表格的行高远大于文字的高度,更糟糕的是文字还是顶对齐,非常不美观。编辑个空文档,然后保存为.ott,转换的时候指定模板文件可解决该问题。
    • 生成的文档的左右边距不一致。恩…,这个我还没有找到解决办法。

大家都来开发游戏?

“flappy bird”到“2048”,再到最新流行的“don’t tap the white tile”。最近似乎一个“很普通的”休闲游戏莫名的就红了,甚至flappy bird的作者都将成功归结于运气。下一个爆红的手机游戏会是什么似乎很难预测。

面对繁荣的手游市场,即使是红的不能再红的红海(任何一个热点游戏都有成百上千的clone),也很难让人不想尝试一下。

想了几个点子,然后去搜了一下,其中一个已经有人做了,而且名字都和我想的一样。不过那个游戏的UI比较烂,下载量也一般。有人有同样的想法或许是一件好事,如果没人做过的东西或许根本就不值得做。

下面总结一下爆红的游戏都有什么特点

  • 容易上手,不需要学习,拿到就就可以开玩。
  • “极限运动”,始终有突破之前成绩的可能。
  • 触屏友好,充分利用手机的特性。注:俄罗斯方块和贪吃蛇这样的经典游戏由于非触屏友好,所以是没机会红了。
  • 碎片时间。可以随时随地的玩。

[Android]标准体重

很早之前就想写个手机APP。之前想写个类似豆瓣客户端的东西,因为我认为手机APP更多的要和网络结合才有价值。
最终写APP的事一直没有真正付诸行动。最近想还是写个最简单的好了,也算是给自己一些鼓励。

标准体重 一个计算BMI指数的应用。算是Android的开发作业,功能非常简单。代码没有写的很规范,没好意思放到GitHub。

下载地址: bmi.apk.zip

注: BMI指数(身体质量指数,简称体质指数又称体重指数,英文为Body Mass Index,简称BMI),是用体重公斤数除以身高米数平方得出的数字,是目前国际上常用的衡量人体胖瘦程度以及是否健康的一个标准。

单元测试的一些体会

以前发布开源项目时被国外的网友一阵鄙视,之后在自己的一些小项目中陆陆续续的有写一些单元测试。你一旦真正接受单元测试,并实行起来还是很容易体会到单元测试的优点的。只是尽管单元测试有很多优点,在国内的环境下写单元测试的公司依旧不多。
单元测试的好不是立竿见影,需要时间的累计。在最求“速成”的环境下,单元测试没法推行也是显而易见的。
单元测试的问题

  • 单元测试并是立即见效的
    单元测试代码的书写也需要花费一定的时间,有时还需要不少时间。如只是从一次测试的效果来看单元测试多半是不划算的。单元测试解决的是之后代码改动之后再测试的时间。如果时间进度紧张,即使知道单元测试的好处也难有心去写。毕竟要先解决燃眉之急,更何况某些代码的生命周期很短,还不足以让单元测试发挥价值。
  • 单元测试的基础测试数据需要积累
    一些复杂的业务系统业务数据之间的逻辑关系会比较复杂。比如你需要有基础的客户数据、员工数据后才可进行合同创建等功能的测试。如果没有前期测试数据的积累,你要直接给合同模块写单元测试。想到要将员工、客户等数据补上,立马就退缩了。(注:其实手动测试也要将员工、客户先创建好,只是因为之前手动测试时相关数据已创建好)

如何开始

  • 在一些小项目中先用,真正体会一下单元测试的优点。
  • 从项目的一开始就使用单元测试。单元测试的书写和项目的书写一样,都是一个循序渐进的过程。
  • 只写一些必要的单元测试,不要最求大而全。即使手动测试你也不可能测试到所有小概率情况,所以可以只写一些最必要的单元测试,让单元测试代码发挥最大价值。

Resolution 2014

已经有挺长时间没有更新blog了,上一篇还是2013年初。有时候想,写blog或许是一种状态,或许某天我的博客也会和互联网上的许多blog一样不再更新。这次这样长时间没有更新blog可能也是生活状态和之前有所不同,只是并不是我所期望的状态。
毫无悬念,2013依旧留下了很多未想做、要做却未做完的事。

  • 陆陆续续的看了一些英语,不过似乎并没起到什么作用,希望之后多少能有些进展。
  • 很早之前就已看过Android开发的东西,但一直没有真正动手做过什么。希望在2014年能做个小东西。

douban的Android客户端

最近想给Android开发一个小应用,找了一些Android应用来做参考。感觉豆瓣客户端挺有意思。

豆瓣为小组、活动等功能开发了独立的客户端。在使用的时候有时候会在APP里直接调用douban的WEB站点或是WAP站点。Facebook放弃HTML5被看成是原生APP的一次巨大胜利。但是WEB APP在跨平台以及开发的便捷性方面都有着一定的优势。在人力资源不是太过充足的情况下采用原生+WEB的开发模式可能是个不错的选择。

在用户使用频繁对用户体验要求高,性能敏感的地方使用原生开发。一些相对简单的页面直接使用WebView即可达到原生应用同样的体验,对这些页面直接使用HTML开发。还有些用户不太常用的功能在前期可以先调用WEB版的原始页面。

PS:

  • 就目前而言用HTML5开发移动应用很难提供很好的用户体验。但我相信技术在发展,HTML5还将是趋势,主要问题还是时间,2年?
  • Android手机上Metro风格的应用越来越多了。苹果将模物化设计做到了极致,接下来是Metro设计极简的逆袭?

京东夺宝岛抢拍脚本

京东的夺宝岛是京东的“瑕疵品”拍卖平台。试着拍了个物品,发现竞争还挺激烈。在拍卖即将结束的前几秒,大量买家争相出价,要想用心仪的价格抢拍到自己的物品并不是一件容易的事。
简单的分析了一下夺宝岛的页面,感觉要实现一个抢拍脚本并不麻烦。找了一下没发现现成插件,于是就自己动手了。
要实现抢拍功能,一般思路是做成浏览器插件。做浏览器插件固然不错,就是写起来稍微麻烦了些。更简单的方式是用“浏览器收藏夹”在当前页面执行脚本。将javascript:void(alert('执行当前脚本'))加入浏览器收藏夹,点击该条目时将在当前页面执行JS脚本。
利用浏览器的这一特性,我写了下面的脚本。修改脚本中的最高出价以及用户昵称,将脚本并成一行,添加到浏览器的收藏夹。进入需要抢拍的页面,点击,然后就会自动帮你出价了。

javascript:void(
  t=setInterval(function(){
    max=450;//你的最高出价,超过这个价格就不抢了
    uid='vicalloy';//你在京东的昵称(总不能和自己抢东西吧)
    did=$('.list-info>li.fore1').text().replace('夺宝编号:', '');
    url="http://auction.360buy.com/json/paimai/bid_records?pageNo=1&pageSize=1&dealId="+did;
    ct=$('.over-time>strong').text();
    if (parseInt(ct)>10) return;//在最后10秒开抢
    $.getJSON(url, function(d){
      p = parseInt(d.datas[0].price)+1;
      cuid = d.datas[0].userNickName;
      if (p>max) {
        clearInterval(t);
        return;
      }
      if (uid==cuid) return;
      $.get("http://auction.360buy.com/json/paimai/bid?dealId="+did+"&price="+p);
    });
  }, 1000))//一秒钟刷新一次价格,如果你希望提高出价的成功率,可将间隔改小。

让Mac启用休眠模式

在windows下除关机外还支持待机和休眠两种“关机”模式。考虑到有人可能并不了解待机和休眠的区别,这里简单的介绍下。

  • 待机
    计算机在待机状态时,内存中的信息未存入硬盘中,计算机可以被迅速唤醒。如果断电,内存数据将丢失无法常唤醒。
  • 休眠
    计算机在关闭前首先将内存中的信息存入硬盘,唤醒时将硬盘上的数据重写回内存。休眠状态下的唤醒速度比待机慢,好处是休眠后可以断电。

在Mac中就只有睡眠一种待机模式了。默认情况下Mac的睡眠相当于windows的待机模式。虽然Mac mini的待机功耗非常低,不过长期待机浪费的电加起来也不少,对产品寿命也会有些影响。实际上Mac已经提供了对休眠功能的支持,稍微做些设置即可将睡眠的默认行为由“待机”改为“休眠”。
Mac的睡眠方式说明:

  • 0 (quick): Default sleep behavior on most Apple computers. RAM is still powered on while
    sleeping. Wake up is fast. Safe sleep is disabled.
  • 1 (deep): Hibernation behavior. System is totally shut down while sleeping. RAM contents
    are dumped to disk. Wake up is slow.
  • 3 (safe): Default behavior on Powerbook HD and later computers. RAM is still powered
    on while sleeping. Wake up is fast. Safe sleep is enabled, so RAM contents are
    also dumped to disk before going to sleep.
  • 5 (deep): Same as mode 1 for systems with encrypted virtual memory.
  • 7 (safe): Same as mode 3 for systems with encrypted virtual memory
  • 注: 我使用5的时候无法正常唤醒,因此不建议使用5&7

查看当前的睡眠模式: pmset -g | grep hibernatemode
修改睡眠模式: sudo pmset -a hibernatemode X

2013年新年期望

龙年最后一天上班,写下对来年的期望。有如新春播下的种子,期待来年能开出绚丽的花朵。

  • 把英语提高些。达到在无字幕情况下轻松看懂美剧的程度
    英语是一种工具,它将影响你的视野。如果可以轻松听懂日常的英语会话,那会对读写等起到很好的正反馈。
  • 工作内容有趣些,认识些有趣的“家伙”,一起做能让自己满意的产品
    让最广大的用户使用自己的作品,解决用户的需求,是一个创造者最到的乐趣。能让用户满意,这至少需要先让自己满意。
  • 收入方面能有所改善
    我一直都没有太多的经济观念,过着一人吃饱全家不饿的生活。但现在已经不再是一个人了。生活的柴米油盐都和¥有关系。有时候¥会影响生活品质进而影响一个人的精神状态。
  • others
    ……

静待花开

随机迷宫生成算法

在线迷宫游戏地址: lbmaze@sae
项目地址: lbmaze@github
最近研究了下迷宫的生成算法,然后做了个简单的在线迷宫游戏。游戏地址和对应的开源项目地址可以通过上面的链接找到。开源项目中没有包含服务端的代码,因为服务端的代码实在太简单了。下面将简单的介绍下随机迷宫的生成算法。一旦理解后你会发现这个算法到底有多简单。

  • 将迷宫地图分成多个房间,每个房间都有四面墙。
  • 让“人”从地图任意一点A出发,开始在迷宫里游荡。从A房间的1/2/3/4个方向中的任选一个方向前进。在从A房间走到B房间的过程中,推倒A/B房间之间的墙。
  • 如果方向x对面的房间已经走过,则选择其他方向。如果所有方向的房间都已经走过,则退回上一个房间看是否还有可选道路。
  • 走到真正无路可走时,说明已经走过了所有房间,迷宫也生成好了。

下面是该算法的python实现(核心部分)

def gen_map(self, max_x=10, max_y=10):
    """ 生成迷宫 """
    self.max_x, self.max_y = max_x, max_y  # 设置地图大小
    self.mmap = [[None for j in range(self.max_y)] for i in range(self.max_x)]  # 生成原始地图
    self.solution = []  # 迷宫解法
    block_stack = [Block(self, 0, 0)]  # 从0,0开始生成迷宫(同时将这点作为起点),将起点放到栈里
    while block_stack:
        block = block_stack.pop()  #取出当前所在的房间
        next_block = block.get_next_block()  # 获取下一个要去的房间
        if next_block:  # 如果成功获取下一走发,将走过的房间放回到栈里
            block_stack.append(block)
            block_stack.append(next_block)
            if next_block.x == self.max_x - 1 and next_block.y == self.max_y - 1:  # 走到终点了,栈里的路径就是解法
                for o in block_stack:
                    self.solution.append((o.x, o.y))
def get_next_block_pos(self, direction):
   """ 获取指定方向的房间号 """
    x = self.x
    y = self.y
    if direction == 0:  # Top
        y -= 1
    elif direction == 1:  # Right
        x += 1
    if direction == 2:  # Bottom
        y += 1
    if direction == 3:  # Left
        x -= 1
    return x, y
def get_next_block(self):
    """ 获取下一要去的房间 """
    directions = list(range(4))
    random.shuffle(directions)  # 随机获取一个要去的方向
    for direction in directions:
        x, y = self.get_next_block_pos(direction)
        if x >= self.mmap.max_x or x < 0 or y >= self.mmap.max_y or y < 0:  # 房间号在许可范围内
            continue
        if self.mmap.mmap[x][y]:  # 如果已经走过
            continue
        self.walls[direction] = False
        return Block(self.mmap, x, y, direction)
    return None  # 没找到有可用的房间

注: 由于采用该方法生成的迷宫道路的分支数量并不是太多,coffeescript版在生成迷宫的过程中增加了随机处理,对应算法也稍微复杂一点点。