Skip to content

mzh

中国县及县以上地区数据结构【Python】

郭嘉统计局虽然经常更新地区数据,但是其数据结构糟糕透顶,plain
HTML有没有!都不提供SQL或者是XML数据类型! 都还得写个解析器来加载这个结构,用LXML解析的过程我就不写了
去除各种table之后的数据库

110000 北京市 110100 市辖区 110101 东城区 110102 西城区 110105 朝阳区 110106 丰台区 110107
石景山区 110108 海淀区

转换以后:

省:北京
   ├─ 市辖区
   │  ├─ 东城区
   │  ├─ 西城区
   │  ├─ 朝阳区
   │  ├─ 石景山区
   │  ├─ 海淀区
   │  ╰─ 平谷区
   ╰─ 县
      ├─ 密云县
      ╰─ 延庆县

规律就是邮政编码!用了groupby和defaultdict这些基本的Python东西 下面是程序啦,用法是直接打省份的名称即可。
当然,函数已经是单独实现的,所以在其他地方用也行啦~


#!/usr/bin/env python
# encoding: utf-8
from itertools import groupby
from utils import ScaleTree
def make_city_tree(path):
"""
 make a tree of province, city, county of China

 :path: path to database
 :returns: tree

 """
def strip_zipcode(item):
"""
 Strip all zipcode from stats.gov.cn
 :returns: city_name
 """
return item[7:].decode('utf8').strip()
with file(path, 'r') as db:
tree = ScaleTree()
provinces = groupby(db, key=lambda x: x[:2])
for pid, province_data in provinces:
province_data = list(province_data)
province_name, cities = strip_zipcode(province_data[0]), province_data[1:]
for cid, city_data in groupby(cities, key=lambda x: x[:4]):
city_data = list(city_data)
city_name, counties = strip_zipcode(city_data[0]), city_data[1:]
tree[province_name][city_name] = map(strip_zipcode, counties)
return tree
if __name__ == '__main__':
tree = make_city_tree('db.txt')
pro = unicode(raw_input('省:'), 'utf8')
for x in tree.keys():
if pro in x:
cities = tree.get(x)
for t, city in enumerate(cities):
if t+1 != len(cities):
st = u"├"
else:
st = u'╰'
print u"\u2004\u2004\u2004%s\u2004%s" % (st, city)
for d, county in enumerate(tree.get(x).get(city)):
if d+1 != len(tree.get(x).get(city)):
st = u"├"
else:
st = u'╰'
if t+1 != len(cities):
ct = u"│"
else:
ct = u"\u2004"
print u"\u2004\u2004\u2004%s\u2004\u2004%s\u2004%s" % (ct, st, county)

最近一些面试题(Python)

最近换工作,面了几家公司,超大,大,中,小都有,不过就不提名字了。 唯一的感触就是自己实在是太浪费四年的时间在游戏上了,要是腾出时间搞算法那是极好啊。
不过我总觉得知识的欠缺是可以通过学习来弥补的,同时,记录也是学习的一部分,现在我就写写我还记得的几道面试题。

骑士巡游问题

一道简单的BFS(广度优先)算法题

在一个国际象棋棋盘上 (NN),有一个棋子"马(Knight)",处在任意位置(x, y);
马的走法是日字型,即其可以在一个方向上前进或后退两格,在另一方向上前 进或 后退一格。 请编程找出马如何从位置(x, y)走到棋盘右下角(N,
N)的位置的步骤。 例如:假设棋盘大小为3
3,马处在(1,2)位置,马只需要走一步, 即 (1,2)-> (3,3)即可到达目的地。

具体代码我已经push到github上了,链接在此
对于从来没有研究过算法的我来说,整个题目的难处在理解队列这个概念上。
广度优先的队列之奇妙的地方在于,每一层的尝试,都先放入队列中,紧接着,挨个判断队列中的元素是否达成了目标。对,就这么简单。

Fibonacci数列

好吧~两家公司都考这斐波那契……

用Python生成指定长度的斐波那契数列

考递归:

def fib_recursion(n):
if 0< = n < 2:
return 1
else:
return fib_recursion(n-1)+fib_recursion(n-2)
print fib_recursion(99) # 调用即可

不错吧~完美地使用了递归,当然,如果能用字典存储,这样更完美了对吧? 错! 其实考的是iterable的使用,Python CookBook
19.3里那极为精妙的解法才可以称得上答案:

def fib():
x, y = 0,1
while True:
yield x
x, y = y,x+y
if __name__ == '__main__':
import itertools
print list(itertools.islice(fib(), 10))

LRU缓存

现场编程题,1小时之内写出可用的LRU cache util

其实当场没有写出来,主要是因为还不清楚list的pop()中如果有数字的话,就是O(n)的复杂度,我当时的水平最多写得出[这个源里的程度](https://github.com/mengzhuo
/django-lru-cache)。用的是dict的popitem,而且是面试官说在生产系统中也使用这样的方法。 后来,仔细看了Python
Cookbook(知道是好书了吧),发现collections的deque神器,不过正反向双list也是个不错的ADT选择,而且这些连面试官都不知道。
具体的应用以后写读书笔记时会和大家分享下的。

Django调试方法三枚

如果各位同学在coding过程中,需要打印变量,捕捉异常,但只能用print变量的方法解决问题,而你觉得不优雅,不方便,急需一种调试的方法的话,那这篇文章很适合你。
我学这些是因为在Django调试中,平时使用默认的Error
Page已经是非常方便了,但是,有次在对外API测试中可能会出现无法查看那个错误页的情况,两眼瞎,四处pirnt,最后还不能解决问题……如此屈辱后,我等痛下决心,一定找到更好的解决方法,因此有了本文。技巧

ipdb断点法[排错/尝试]

在你想打断点的地方,加上这句—-需要安装ipdb

import ipdb;ipdb.set_trace()

[](blog/wp-
content/uploads/2012/11/2012-11-17-213312的屏幕截图.png)

当程序运行到这里时,在启动服务器的console中将会弹出上面的提示符。这时就可以在这里像使用console编程时一样地随心所欲了~
顺便推荐几个好用的快捷键

按键 含义
l line:查看指定行,例如l 31
r return:执行到当前函数返回
c continue:继续执行代码,直到下个断点
whatis 查询某个变量是什么

有了这招,基本的疑难杂症都可以debug掉了~

logger大法[定位/记录]

这是今天的主角,相对上面的方法,这个的自动化程度不是一般的高,在不改变源代码的情况下就可以记录所有的django行为。
在Django的settings.py里面,可以看到下面这自动添加的LOGGING这个设置变量。默认情况下,Django只会在DEBUG=False时,当服务器遭到500错误,才会发邮件给admins。而我们现在要在代码中加入一个logger,这样,任何错误或者是希望打印出来的信息都不会逃走啦~而且还可以自定义等级记录哦~
在settings.py中

from logging.handlers import SysLogHandler
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
"syslogHandler":{
"class":"logging.handlers.SysLogHandler",
"formatter":"verbose",
'address': '/dev/log',
"facility":SysLogHandler.LOG_LOCAL6
},
},
'loggers': {
"project":{
"handlers":["syslogHandler"],
"level":"DEBUG", #整个project的logger等级为DEBUG
},
"project.app":{
"handlers":["syslogHandler"],
"level":"INFO", #而project下的app对应的是INFO等级
},
},
"formatters":{
'verbose': {
'format': '%(levelname)-8s[%(name)s][%(funcName)s:%(lineno)d] %(message)s',
},
}
}

在某些你想记录log的地方

import logging
logger = logging.getLogger(__name__)
logger.error('This is an error')

注意这个__name__,返回值是app+文件名,有利于我们定位bug和使用不同的handler,比如下面的传递路径示意:

[](blog/wp-
content/uploads/2012/11/2012-11-20-220942的屏幕截图.png)

如果一条log信息被project.app.views的handler抓住了,并且,propagate=False时,父级的handler,比如"project.app",就不会收到此log。
至于logger语句放在哪里,我个人习惯跟着try except这段,用于捕捉异常,或者在方法开始时logger.info查看相关参数。
而logger默认等级有6种:NONE, DEBUG, INFO, WARNING, ERROR, CRITICAL(严重等级由低到高)
至于logger输出目的地,有很多种选择,可以输出到文件,邮件,http请求,也可以输出到syslog中。本文的例子就是输出到syslog中。
至于syslog的配置,网上也有不少好的文章,这里就不说了 当然这些logger等级时会在%(levelname)这个变量中出现,而一般的tail -f
最多只是输出他们,并不能主观地区分他们。所以需要另一个工具进行等级的划分和关键词的渲染(syntax
highlighting)。这里就严重推荐grcat这个小工具,能正则匹配,关键字各种格式化。

![日常例子一枚](/2012/12/2012-12-16-145748的屏幕截图-
610x214.png)

grcat的配置也十分简单,也有很多样例,网上可以搜到的。这里也不多说啦~ 讲错的地方,欢迎拍砖

Virtualenv和pip小探

本文献给那些看着参差不齐的中文文档/教程,但还在坚持折腾以上两个工具的童鞋。

声明:本人也是菜鸟,真正的有用的概念解释,请参看官方文档,以下都是我的个人理解。

virtualenv

这里是导言吗? 用过Python的同学,肯定会对Python及程序的版本之间经常更换的api感到痛苦不以。就拿我折腾的Django来说吧,公司服务器上跑的是Django1.3、同事也是用1.3开发,但是因为我是新来,一个pip install django下去,就是1.4.2。好了,你自己写的Django Project自然没有问题,自己本地测试也没有问题。但是要和其他人交流的时候就蛋疼了,因为你的1.4.2跑不了1.3的程序……当然,这时,你可以选择卸载自己本地的Django,换成1.3,等你要重新测试自己的Django,怎么样,扯着蛋了吧。为了解决以上问题,virtualenv横空出世了。 正文 为了解决以上蛋疼问题,我们需要安装virtualenv。 sudo pip install virtualenv 安装好了以后,就可以在任何目录下新建一个virtual-environment(我更喜欢叫:盗梦空间),当然一般我习惯在项目的边上创建一个$project_name-env。例如: virtualenv demo-env 这下,只需要 source demo-env/bin/activate 就可以激活这个虚拟环境了。如下图所示: 注意到括号里的(demo-env)了没?对,我们已经进入了第一层梦境demo-env。 回到导言里的问题,我们需要在这里建立一个使用django1.3的环境,不让它干扰我们的本地环境。 pip install Django==1.3就可以了,然后在里面运行别人的Django程序,没有问题!装很多python程序,没有问题,这些都不会干扰你的本地环境,放心破坏吧!

PIP

刚才的virtualenv的介绍里已经说了pip的安装命令了。但你当pip只是个安装工具的话,那就大错特错了。pip还有一个更重要的功能—-
冻结当前梦境开发环境版本(freeze)。 pip freeze 看到了刚才安装的django
1.3了没有?这就是我们目前的生产环境,把这输出结果写到requirement.txt里,别的程序猿们就知道你的程序至少在这个环境下可以跑,这时他们拿着requirement.txt,新建个virtualenv,pip install -r requirement.txt 就可以运行你的程序了。 pip加速安装
通过pip安装程序,还可以用git和svn的方式,具体操作网上也有。但是对于懒人来说,直接用pip是最好不过的了,但是,有时这会很慢!!!慢到你抓狂。当然
,和apt-get一样,pip也可以换源,或者叫添加镜像。 新建一个~/.pip/pip.conf,往里面写入

[global]
index-url=http://e.pypi.python.org/simple
timeout = 30 require-virtualenv = true
download-cache = /tmp
[install]
use-mirrors = true
mirrors = http://d.pypi.python.org http://b.pypi.python.org http://c.pypi.python.org

下载应该就会像飞了一样,配置文件过于清晰,所以我也不用多说了吧 :)

Django开发笔记[Month 1]

电脑的笔记有两个好处:一、可搜索,二、扩展方便。
入职一个月了,也该整理整理了,给自己,也给大家分享一下哈(再次鄙视国内的IT技术文档/问答环境,抄抄抄,Cao你妹啊,你们TM自己写点不行啊!!!!)。当然我刚学Django/Python,也欢迎指点错误的地方。

概念层面

概括

Django是属于MVC的Web框架。 Model:负责与数据库打交道 View:负责获取或者增强从Models得到的数据
Controller:这是Django本身

Project与App的区别

Project:提供各种配置文件 App:功能的结合,包括Model和view,需要在根目录下添加"init.py",使得Python可以识别

ManyToMany与ForeignKey的区别

ForeignKey例子: 员工打卡上班的记录—-员工可有多次打卡记录,但是一次刷卡记录只能有单一员工属性(一对多) ManyToMany例子:
一篇文章可以有多个标签,而一个标签对应的文章也可以是多篇的(多对多) ManyToMany还有一种特殊的结构,非常类似Twitter的Follow。 假设
A Follow了 B,但是B并没有Follow A,他们之间的Follow是不对等的,要实现这种关系,要在Meta里面设置symetric=False

SexyCode

这是我理解后觉得"性感到死"的一些代码:

  1. lambda x, y: x+y
    Lambda其实和JS中的匿名函数有这异曲同工的作用,嫌起名麻烦,就丢进去,Lambda默认返回里面的值,所以不需要return

  2. map(lambda x: x+1, [1,2,3]) #得到[2,3,4]
    也就是每个都执行一次前面的函数

  3. reduce(lambda x, y: x+y, [1,2,3]) #得到6
    也就是每个都和之前的元素执行一次操作

  4. [i for i in xrange(0,100) if i%2==0]
    得到100以下所有偶数。学术上叫"列表推导",但在现实案例中是遍历的好工具。

  5. Q(**{key:value})
    我在培训的时候,学到的是Q、F这两个Django函数是不接受字符串的,即Q("abc=1")是不能接受的。但是这段代码性感之处就是彻底摆脱了这条束缚(其实就是重写了keyword对),可以随意构造你喜欢的查询段,这在构造搜索功能时十分有用。比如:

    q_dict = reduce(lambda x,y: x&y, [ Q(**{"%s__slug_name" % taxonomy:request.GET.get(taxonomy)}
    ) for taxonomy in request.GET.keys() if taxonomy in SEARCHABLE_LIST ])

  6. @property
    这个@符号的用法叫"修饰器",个人感觉这是python比其他语言优美的地方,如何构造修饰器的话,还是看文档的好。这里只是说在class中使用@property的话,这个函数就自动地变成class的属性了,这和js的set、get很像

  7. super超类的使用,super一出,继承的子class一概不执行自身的函数,而是执行super指定的函数

  8. annotate和aggregate。这两个家伙在构造新的query_set时非常有用。比如要统计出一台电脑的总价时,在ComputerManager里面使用

    def get_query_set(self):
    query_set = super(ComputerManager, self).get_query_set()
    query_set = query_set.annotate(price=Sum(‘devices__price’))

这样,每台电脑就有了总价格。这在构造商品集的时候很方便,但是admin.py的编写就略显罗嗦了。

Error集

  1. 取出数据时:XXManager object is not iterable
    这是因为Django不会在执行代码过程中得出SQL语句并查询(所以想获得Sql语句也是不可能的)。所以需要使用.get(),.all(),.filter来获得真正的数据实体

流水记北漂初

在求职公寓房东那儿退房时,才发现自己只住了短短的12天,可这12天,发生了太多事,做了太多第一次,总是感觉已经有一个月了。
刚到北京时,手机电池就挂了,我亲爱的ME525啊,你不是防水的吗,怎么就这紧要关头给我倒下了呢……因为之前只查到了求职公寓的大概地址,只知道要到双井桥下,到了地,问了一路的人"XX公寓"怎么走,都说不知道,还有个好心的大爷说小心别被骗了。当时,我拖着重重的行李已经走了4站地,浑身是一路火车上下来的汗臭,最后不知所措地站在地铁站边上,要是没有喝下那瓶从超市里买来的矿泉水来压压神,估计就要坐在地上抱头痛哭起来,然后边哭边骂自己是个2B
—-怎么这么傻缺地一个人闯来了北京。
幸好我理智尚存,在附近一手机店买了块飞X腿电池,这才打通了电话,问到了之前查的求职公寓的详细地址……这下总算找到地儿住了。所谓「求职公寓」其实就是大学男生宿舍的加多倍蟑螂加臭加人版,刚进去,一兄弟正收拾东西,稍微聊了下才知道他今天正好走,告诉了我哪架床比较好。另一个陕西的哥们(就叫超哥吧)也是昨天才到,他们两正聊着那四个同房间的面点师。稍稍整理后,我在那窄得不能再窄的浴室洗了澡,然后踏上北京地铁晚高峰这条不归路去北交找岳兄了。这从劲松到西直门,整整花了2个半小时……疲惫不堪地坐到了地以后,见到岳兄那是万分高兴(总算见到认识的东西了),吃了晚饭,这才聊些「家常」,还跟岳兄参观了下他「科学的殿堂」—-实验室。岳兄心细,知道我肯定是回不去了,就打点着让我住下了。其实我都没反应过来,天就亮了,可看着充不进电的手机,这下可真得找台替代品了。又蹭了岳兄一顿午饭后,我等便酒足饭饱地回去了,这下才第一次见到了四位面点师。
几天接触下来,这四位面点师是彻底封住了我想叫苦的嘴,因为他们更年轻,刚刚20岁,就得出来觅工,早上5点半起床坐公交车(天没亮,地铁也没开门),一周只有一天休息,工资也不高
—-他们买不起自己亲手做的面包。他们常常聊以前打CF的趣事,却实在是累到没有精力去玩,往往匆匆吃过晚饭点便睡下了。
而我那些天就是不停地网投简历,头天就面试了2家,天网面试是早上,我刚到就被这里的工作环境吸引了,那种绿树成阴,还有人在晨练的感觉和家里很像,而整个办公楼道里也充满着「不差钱」三个字。愉快的面试后,张先生说等电话。我也就心满意足地跑到国图去蹭噌知识的味道了。其实我没有那种去图书馆的习惯,从我在家一年基本没去过区图书馆就可见一斑,这次完全是为了满足我土鳖心态,来看看周主席盛赞的地方。刚进去我就晕掉了,能在天网工作的话,我天天都要来,没别的,就图这儿的空调、水、网统统免费啊!俺那是充分地当了一把纳税人。
下午就去了著名的798,在那的点点网是干干脆脆地拒掉了我。第二天又收到了极客公园的电话,和技术部的老大畅谈之后,他也说等电话吧。
接下来就好像运气用完了一样,足足一星期都没有面试通知,倒是在国图咖啡馆里吃了不少西餐、喝了各种从来没喝过的咖啡……要我排个好喝顺序的话:香草拿铁>普通拿铁>冰拿铁>卡普奇诺>美式。也算是土鳖了23年之后,体验了把下午喝喝咖啡,晒晒太阳,打打电脑这种小资生活吧……
当然我正事也没误,期间还去了趟招聘会。招聘会上投了两家都没回音,而招聘会上有一堆人,像电脑城里卖电脑的人一样招各种销售。搞得我不停地说"不用了,谢谢"。倒是里面一兄弟还说:「放着月薪1万不做,去给别人打工,你是真傻还是假傻啊。」我回了句「Stupid
is stupid
does」,可能是听不懂,他也不再缠着我了。回到公寓就听说了超哥找到了份经理的工作,正在试公司发的衬衫,然后他拿出件「1800然后打半折」的西服,还说这是结婚时侯买的。接着他就搬到公司提供的宿舍去了。

最后推友招聘的工作我试着应聘,最后通过5个人轮番的面试,周五,在叫Hawaii的会议室签了厚厚的合同书,终于得到了份工作。
因为公司在苏州街,和公寓正好是10号线的两头,前年还在上海的时候,深刻体会了天天坐地铁上班的痛苦的我决定在苏州街附近找。然后四处打电话,接着,第二天我就看到了正宗的隔断间,房间阴暗潮湿,看房时,房东女儿和一男人还躺在床上呢……搞得我像捉奸似的。这地肯定我不能住,要是住上半年,我估计就直奔回家里了。出门后,在胡同口正找着下一家的时候,一个好像是中介的女的就带我去了小南庄里一民居,说实话不是隔断间已经不错了,人很少,还有窗,一问才800,当时就想住进去了。不过因为约好了另一家,所以就直奔了「怡水园」,走到门口时,我都不敢相信,大门两边是柳树,还有保安,一看就是高档小区,心想就算900也住了。然后看房时,第二次捉奸,一问房东才知道,这对小情侣只租到20号。之后又看过房东另外小区的房子,但是还是觉得,为了每天工作回来有点柳树做安慰,感觉像个有高级公寓住的成功青年(当然最重要的是这间竟然是最便宜的,还带窗),就决定住「怡水园」了。痛心地交了「押一付三」之后,终于在北京有个临时落脚的地方了……
当天就搬了过来,退房时竟然还有点不舍得求职公寓里房东的猫和那超大的阳台(因为在顶层)。到了新地,各种买东西,什么床单啊,被子啊,扛得我是各种蛋疼。铺好床后,躺在床上,我就想"自己一个人生活真心不容易,以后有个家还不懂得珍惜的话,那真是心被狗吃了。"

[Gnome-Shell扩展制作指北]第三篇:图标与图片加载

看了题目你可能会奇怪,这有什么不同吗?其实,这里只是两种不同的加载方式。

图标

第一种是图标,或者叫icon,加载的方式是通过系统搜索整个主题库来加载这一图标文件。很难懂?没关系,在/usr/share/icons/下面有很多的图标是吧,那咱们就来加载
face-smile效果图 和上节课有神马不同呢: git diff Lesson_2 Lesson_3_Chapter_1

//…此处省略一些………
+const Gtk = imports.gi.Gtk;
@@ -17,7 +18,17 @@ ExampleIndicator.prototype = {
PanelMenu.Button.prototype._init.call(this, St.Align.END);
this.label = new St.Label({text:"This is Example"});
- this.actor.add_actor(this.label);
+ this.label.add_style_class_name('example');
+
+ //this.actor.add_actor(this.label);
+
+ this.iconTheme = Gtk.IconTheme.get_default();
+ this._icon = new St.Icon({ icon_type: St.IconType.SYMBOLIC,
+ style_class: 'popup-menu-icon',
+ icon_name: 'face-smile',
+ icon_size: 26
+ });
+ this.actor.add_actor(this._icon);
Main.panel._rightBox.add_actor(this.actor);
},
//…此处又省略一些………

iconTheme = Gtk.IconTheme.get_default(),这里是告诉GTK主题系统去寻找当前的主题。也就是「主题设置会影响你的图标显示」。用户换了一个主题,你的图标也有可能改变哦。
你可能要问,如果要添加自定义的图标路径怎么办呢? 这也好办,跟Gtk主题说一声就行了: iconTheme.append_search_path (自定义的图标路径); St.Icon实例的基本组成:

  • icon_type:图标类型,也就两种,全彩(正常的),象征性的(例子里用的)
  • icon_name:图标的名字,这里我们挑了face-smile :)
  • icon_size:图标的大小,要注意这个大小指的高和宽同样的值,如下图

这里要注意图标的命名,如果你选择了象征性的图标,就要在图标文件名后面加上"-symbolic"这样的后缀,例如:face-smile- symbolic.svg 这样加载图标确实很方便,特别是系统本身自带的那些图标上,但是要是加载图片时,总不可能把图片塞进主题里对吧。

CSS式加载图片

当然不只是换了个图片,让我们看看它和Chapter
1有神马区别。 git diff Lesson_3_Chapter_1 Lesson_3_Chapter_2
除了添加了一个新文件以外,实际上只有短短的8行:

+.example_icon{
+ background-image: url(pic/applications-science-symbolic.svg);
+ background-size: contain;
+ width:26px;
+ height:26px;
+}
+ //this.actor.add_actor(this._icon);
+ this._css_icon = new St.Bin({ style_class: "example_icon" });
+ this.actor.add_actor(this._css_icon);

如果你写过CSS,这些对你简直就是小儿科了。不过我还是想罗嗦一下:
首先我们添加了一个St.Bin(暂且把这当容器用吧),然后给这货添加了一个"example_icon"的类。并在css里定义了这个类的表现形式,和第二课里做的其实差不多吧。这和图标式加载有些许不同,那就是Bin的大小不受父级元素控制哦。
其实加载图片还有两种方法,不过我都不推荐:

  • Clutter.Texture.new_from_file,因为要考虑绝对路径的问题,而css可以考虑相对路径
  • GdkPixbuf,actor不能接受这个实例,添加时要转换

当然你觉得好,你可以用哈~不过我觉得样式和程序分离是未来的设计趋势,再说了,CSS就是方便呗。

总结

本节我们学习了:

  • 图标通过GTK主题系统加载
  • 图片通过css加载

这样一来,大家就可以加载自己喜欢的GUI啦~

[Gnome-Shell扩展制作指北]第二篇:文字显示

文字显示在各种面板类插件几乎是必有的项目,比如默认在Gnome-shell的面板正中央就是一个文字类的时间显示器。
废话不说,直接上实现效果:

代码如下,或者去[GITHUB上下载](https://github.com/mengzhuo/gnome-shell-
example/zipball/Lesson_2):

const St = imports.gi.St;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
let metadata,indicator = null;
function ExampleIndicator(){
this._init();
}
ExampleIndicator.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function (){
global.log('EXAMPLE:Enabled');
PanelMenu.Button.prototype._init.call(this, St.Align.END);
this.label = new St.Label({text:"This is Example"});
this.actor.add_actor(this.label);
Main.panel._rightBox.add_actor(this.actor);
},
destroy: function(){
global.log('EXAMPLE:Disabled');
Main.panel._rightBox.remove_actor(this.actor);
}
}
function enable() {
indicator = new ExampleIndicator();
}
function disable() {
indicator.destroy();
}
function init(metadata) {
global.log('EXAMPLE:%s Initialized '.format(metadata.uuid));
}

你可能会惊呼怎么比第一篇多了这么多代码,简直就反人类嘛! 其实,还是差不多的。看看上次我们说的enable函数,里面是不是变成了

indicator = new ExampleIndicator();

至于为什么这么构造,可以看具体文章「ECMAScript
定义类或对象

借助这种方法我们调用了PanelMenu.Button.prototype这一原型,然后把它放入这一原型的actor中。Actor是clutter中的概念,具体可以看其参考文档「[ClutterActor](http://docs
.clutter-
project.org/docs/clutter/stable/ClutterActor.html)」,不过我个人理解成「要显示的东西都往这塞吧」的容器。
我们这次要放一个文本显示框进去,所以以下代码来构造一个新的St.Label(和GTK里面的很像吧:)

this.label = new St.Label({text:“This is Example”});

这样就好了,是不是很简单呢~

小小的扩展—-CSS控制

用GJS显示文字这么简单,但是这和python到底有什么不同?这就是接下来的部分要讲的啦~用CSS控制其样式! 把上面的创建St.Label实例的代码改成

this.label = new St.Label({text:"This is Example"}); this.label.add_style_class_name('example');

是不是很眼熟?对,就是web前端经常干的事,给DOM添加类。 然后你也猜到了—-修改stylesheet.css呗

.example{color:red;}

Alt+F2后,输入「rt」就可以重载css,不需要重载整个G.S是不是更方便了呢! Vala~!

总结

本章我们学习了文字显示,大家还可以用上篇的LookingGlass来调查这个St.Label实例里还有什么可以玩的,下篇是「图像和图标显示」哦~

[Gnome-Shell扩展制作指北]第一篇:架构简析与简单调试

写给那些和我一样看着网上吹水Gnome-Shell用了CSS+JS这一神奇架构后,想跃跃欲试地参与扩展制作的菜鸟程序员和恐惧编译的前端们。

首先,欢迎来到没有编译、没有烦人的GTK控件设计的新世界。由于Gnome-shell(简称G.S)或者叫Gnome3用了Gnome
JavaScript(GJS)作为运行环境,所以可以轻松加载各种GJS写的扩展程序(extension)。要加载这些扩展,总要搞懂扩展的组成吧~

扩展的基本构成

至少需要2个文件:

  • extension.js: 负责插件的核心文件,工作全包
  • metadata.json: 插件的描述文件,额外数据加载
  • stylesheet.css: 负责外观描述

这里我们就用一个简单的例子来说明:

metadata.json

{
"uuid": "[email protected]",
"shell-version": ["3.4"],
"name": "Example",
"url": "//mengzhuo.org/gnome-shell-example",
"description": "This is an EXAMPLE for gnome-shell extension"
}

一些解释(其实要是你英语够好,解释都可以跳过了):

  • uuid是扩展唯一ID的标识,相当于身份证号码一样,必须有"@"
  • shell-version可以运行的Shell的版本号,本身是个数组
  • name是扩展的名字
  • url告诉用户,可以在哪里搞到这个扩展或者介绍
  • description扩展描述

extension.js

function enable() {}
function disable() {}
function init() {}

这三个函数是固有的,不写的话插件会各种报错,不过只要定义了就没问题啦~,其中:

  • init函数正如其名"initialize",初始化用,扩展系统加载时运行一次
  • enable函数是给扩展开启时用的

小试牛刀

现在把这两个文件放到~/.local/share/gnome-shell/extensions/gnome-shell- [email protected]/ 这里你可能注意到了,“gnome-shell-
[email protected]"和metadate.json中的uuid描述是一样的,这么写是为了符合G.S的扩展系统的设定:如果文件夹名称和uuid描述不符,就不加载。

使用LookingGlass调试

按下:ALT+F2开启命令模式
这时输入r重载(reload)整个G.S,当然包括你刚才写的扩展。 然后在终端运行Gnome高级配置工具gnome-tweak- tool找到刚才写的扩展:Example,当然,开启它。 ![](/2012/06/2012-06-24-144436的屏幕截图-
377x300.png)

这时,你写的扩展其实已经加载完成了,什么都没有发生是吧……那是因为我们什么都没有写。 打开extension.js,改成这样:

function enable() {
global.log('EXAMPLE:Enabled');
}
function disable() {
global.log('EXAMPLE:Disabled');
}
function init(metadata) {
global.log('EXAMPLE:%s Initialized '.format(metadata.uuid));
//把metadata.json里的内容加载进来,并输出uuid
}

再次重载G.S,不过,这次再进入ALT+F2命令模式,输入"lg”(是LookingGlass简写,不是老公)
然后选择"Error"标签页,就可以看到例子已经做了点什么。 ![](/2012/06/2012-06-24-151358_1280x800_scrot-
450x285.png)

结束咯

你的第一个扩展就这样写好了,是不是很简单啊,下次要讲是文字显示哦! 本篇代码下载点:<https://github.com/mengzhuo/gnome-
shell-example/zipball/Lesson_1>

开站三周年

不知不觉中,距离那个蝉声不断的午后有三年了。
翻翻记录才发现,三年间口水文写了不少,算上这篇正好140篇,相当于两周一篇,也不枉当年定下的"写周记"的想法。写周记这个不敢说是习惯,但是和初中时语文老师的教导还是有一定关系的
—-当年高文老师规定每周要写周记,我也就每周写点口水话糊弄过去—-
现在翻开看当时的周记自己大牙都笑掉了,而且因为我是个平凡的中学生,每天生活也很枯燥,所以还有不少周记是臆造的。当然下面这篇是真人真事。
对于这么难看的字老师还这么认真地批改,衷心地感谢老师,如果是我早就写上"阅"了。

凡事认真,不是我的专长,和我这人喜欢嬉皮笑脸和不计后果有关。不计后果,三年间写了很多脑残文来批各种自己不喜欢的东西,学校啊,公司啊什么的,我也不打算删掉,毕竟这是年轻过的表现;我也有点担心万一哪天我成了名人,有人来这挖坟,可能有些会尴尬

尽管可能性很渺小,总归是"人怕出名猪怕壮"。不过就算发生了,“凡存在皆有理由”,估计我也不怎么会争,和佛教那万物间没有对错上下,只有羁绊的指导思想有点像。
说到关系,写博客虽然在这个微博、SNS盛行的年代很孤单,还有被现实生活窥探的感觉,就像某博友说的"吃力不讨好";但也交到不少朋友,长了不少眼界,学了不少新东西。比起整天看别人的状态、分享、生活,在个人博客上有种"我得思考"的感觉,而不是被动地接受外界的信息。还有,我想舒淇放弃网络是因为她没有认真写博客,而是依靠微博;看看韩寒,有了博客,别人怎么骂都可以坚持下去,因为还有能做主的地盘,看你丫不爽还能删了你的留言,就算开骂,骂声还能比别人大。冲着这三点,不间断地写个人博客就已经很超值了。
再有在毕业到现在也有一年时间里,虽然我在口语考试中对帅哥考官用蹩脚英语斩钉截铁地说,光看电视是种浪费时间,可我不知道自己这样近近啃老学习的时间是不是也浪费了。看看同龄人,大多已经开始辉煌的人生,买车的买车,结婚的结婚,让我倍感光阴似箭,流时无情,一年真的可以改变很多。估计我这样的人在同龄人里也算差得peerless了。
牢骚太多妨碍心情,不如就此打住,开始努力吧。