简单实现 Python 有序字典(Ordered Dict)
Python 的 Dict 类型很好用,不过有一点可惜就是它的 keys() 是乱序的,想要用它来保存有序的 key-value 对(比如配置文件)就比较困难,碰巧我的毕设就要用到这样一个类型来存放配置文件,怎么办呢?
搜索了一下,Python 2.7 / 3.1 才有 Ordered Dict 的支持,我总不能去 Python 源码里面拽吧……
还有些方法比较陈旧,继承自 UserDict,不支持 iter*() 系列方法,虽然不碍事,不过心里还是不舒服。
不过找来找去,还是让我找到了相对比较简洁,功能有符合要求的代码,见下:
from UserDict import DictMixin
class odict(DictMixin):
def __init__(self):
self._keys = []
self._data = {}
def __setitem__(self, key, value):
if key not in self._data:
self._keys.append(key)
self._data[key] = value
def __getitem__(self, key):
return self._data[key]
def __delitem__(self, key):
del self._data[key]
self._keys.remove(key)
def keys(self):
return list(self._keys)
def copy(self):
copyDict = odict()
copyDict._data = self._data.copy()
copyDict._keys = self._keys[:]
return copyDict
代码来自 ActiveState ,PSF 许可。
在自己的 GTK+ 程序中加入 RGBA 透明支持
上次介绍了怎么在 Arch Linux 下启用 RGBA 透明窗口 ,然而,目前支持 RGBA 的程序还很少,看起来有的透明,有的不透明,很不爽吧?
另外,GTK+ 默认是不启用 RGBA 支持的,我们自己编写程序的时候,怎么加入 RGBA,达到这种酷炫的效果呢?
细心的同学可能已经发现,上次我发的图中最顶端那个程序就是我自己写的,而且已经实现了半透明效果。
加入 RGBA 效果的方法并不难,仅仅是几条语句而已,看看下面的 patch:
From fb88cb790a08e928c5e6656f8334264c5ae9f93a Mon Sep 17 00:00:00 2001 From: Kevin Lange Date: Wed, 5 Mar 2008 09:26:53 -0500 Subject: [PATCH] Added RGBA colormap support to the GUI --- ccm/Window.py | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/ccm/Window.py b/ccm/Window.py index 09306da..84f1d76 100644 --- a/ccm/Window.py +++ b/ccm/Window.py @@ -42,6 +42,10 @@ class MainWin(gtk.Window): def __init__(self, Context, pluginPage=None, categoryName=None): gtk.Window.__init__(self) + self.gtk_screen = self.get_screen() + colormap = self.gtk_screen.get_rgba_colormap() + if colormap: + gtk.widget_set_default_colormap(colormap) self.ShowingPlugin = None self.Context = Context self.connect("destroy", self.Quit) @@ -99,6 +103,7 @@ class MainWin(gtk.Window): self.ToggleCategory(None, categoryName) def Quit(self, *args): + gtk.widget_pop_colormap() gtk.main_quit() def ResetMainWidgets(self): -- 1.5.2.5
呵呵,这是 CCSM 的 RGBA 补丁,看到了吧,其实关键的也就那么几行而已,在初始化的时候试着载入 RGBA 颜色表,在退出的时候对颜色表进行出栈,其他语言也是这个道理。
有了这几行代码,是不是想把桌面上的所有 GTK+ 程序 hack 掉?
呵呵,赶快在自己的程序里加入 RGBA 支持吧!
Python 版 Linux 下的迅雷
Linux 下该不该有迅雷,这个问题一直存在分歧,在此也不予讨论。不过,迅雷抗死链的作用是巨大的,这点是不容置疑的,很多人确实用得着。
有需求就有市场,于是乎,Ubuntu 中文论坛的一位放出了 furl 这个小程序,不但可以解析迅雷的 thunder:// 协议,还可以返回迅雷候选地址, xiooli 大侠更是做出了 Shell 脚本,自动调用 aria2 进行下载。
可惜的是,furl 是 32 位闭源程序,所依赖的 lib32-libopenssl2 在 Arch Linux 下面安装不了……
突然想起,前些日子,可可熊大侠不是写过一个 pythunder 么?干嘛不用这个下载呢?于是就有了下面的程序……
#!/usr/bin/env python#pyaria2.py import os, sys, urllib def usage(): print """Usage: python tharia2.py [OPTIONS] URL OPTIONS: As same as options of aria2c""" def get_url_list(url, listpath): if not os.path.exists(listpath): print "Getting URL list, please wait..." f = urllib.urlopen("http://cocobear.info/demo/pythunder/?url=%s" % url) lst = open(listpath, "w+") lst.writelines(f.readlines()) f.close lst.seek(0) else: print "Found existing url list: ", listpath lst = open(listpath) url_list = [line[:-1] for line in lst] lst.close() print "Recieved %d url(s)." % len(url_list) return " ".join(url_list) def download(url): for prefix in (r"http://", r"https://", r"ftp://"): if url.startswith(prefix): break else: print "Invalid URL: %s" % url exit() listdir = os.path.expanduser("~/.tharia2/list/") listfile = os.path.split(url)[-1] + ".list" if not os.path.exists(listdir): os.makedirs(listdir) listpath = os.path.join(listdir, listfile) url_list = get_url_list(url, listpath) cmd = " ".join(("aria2c -c", " ".join(sys.argv[1:-1]), url_list)) print "Executing command: %s" % cmd if not os.system(cmd): os.remove(listpath) if __name__ == "__main__": if len(sys.argv) > 1: download(sys.argv[-1]) else: usage()
很简单的一个脚本,呵呵,参数和 aria2 是一样的,区别只在于对于 url 的处理(暂时 url 只能放在命令行的最后)。
比如下载 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe,就运行:
python tharia2.py http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe
默认 aria2 可以支持 5 线程,如果你想改为 10 线程,那么:
python tharia2.py -s 10 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe
指定下载目录,用 -d:
python tharia2.py -d "/home/iven" -s 10 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe
更多用法详见:
aria2c --help
目前的主要问题是,可可熊大侠的网站相应速度太慢了,过半分钟才会返回候选列表,汗……不知道是不是我的网速问题,大家可以试一下。另外,就是没有解迅雷的 thunder:// 协议了,还有快车什么的,这个貌似不难,有时间研究一下。
现在还没有开源版本的迅雷候选地址搜索工具,主要大家怕流传太广,遭到迅雷封锁。但是还是好想看看代码是怎么写的啊……
最后,项目的地址:http://github.com/iven/tharia2/
Python – 你可能不知道的
最近决定系统学习 Python,于是把去年 ChinaUnix 的赠书《Python 核心编程》掏出来,使劲啃…… * 跟 C 不同,连续的判断:
* 跨平台,平台自适应的换行符(\n、\r\n……):
* `foo`的作用和 repr(foo) 的作用是一样的(虽然``已经不推荐使用) * Python 中存在复数这个类型 * 幂运算符 ** Python 还有很多不知道的啊,继续学习……
通过这几天的学习,总算是看完了前六章,发现许多以前在《Dive Into Python》里面没有提到,或者很少提到,或者很容易忘掉的特性,在此记录一下~呵呵,感谢 CU 的赞助,感谢 CCAV,感谢 GFW,感谢 宋吉广 的人品~
* 首先,交换 x, y 的值:
x, y = y, x
if x > y > z:
os.linesep
* str()、tuple()、list()等是工厂函数,用来生产对象,而不是简单的强制转换
* id() 函数用来查看对象的唯一标识符
* int 型会在必要时自动转换成 long 型
* 字符串和 tuple 一样,是不能改变的,例如下面的代码执行会出错:
str[0] = 'a'
* // 可以用来做整除(地板除):
>>>1.0 // 2.0
0.0
* 对于 list,extend() 比 + 快
* 可以用 string.Template() 实现 Shell 的 ${变量} 的功能
* enumerate() 函数可以用来产生序列的序号
* tuple 可以用来作 dict 的 key
* copy.deepcopy() 可以用来做深拷贝
使用 PyGTK 和 Cairo 编写一个简单的时钟
呵呵,其实说这个是个自定义控件也不为过。
先介绍一下 Cairo:Cairo 是一套提供设备独立的矢量图形 API 的二维图形库,支持很多不同的后端,如果可能的话,还可以使用硬件加速绘图。Cairo 是用 C 语言编写的,但是提供了很多其他语言的绑定,Factor, Haskell, Lua, Perl, Python, Ruby, Scheme, Smalltalk 等等。Cairo 基于 LGPL 和 MPL 双许可,这意味着你可以用它开发闭源软件。Cairo 是自由软件。
关于 Cairo 的更多详情,可以参照维基百科。
Cairo 的使用方法很简单,跟中学时学习的 LOGO 语言差不多,这里用 GTK+ 编写一个时钟出来。
首先,建立一个窗口。GTK+ 提供了一个专门用来画图的控件 DrawingArea,我们继承它,建立一个类 CairoDrwa,并且把一个对象加到窗口上去。虽然 GTK+ 本身提供了不少绘图功能,不过这里只说 Cairo。
import gtk, glib, math, time class CairoDraw(gtk.DrawingArea): """Drawing with Cairo""" def __init__(self): super(self.__class__,self).__init__() class MainWindow(gtk.Window): """Main window of the test program""" def __init__(self): super(self.__class__, self).__init__(gtk.WINDOW_TOPLEVEL) self.set_title("Cairo test") self.connect("delete-event", gtk.main_quit) cairo_test= CairoDraw() self.add(cairo_test) def main(self): self.show_all() gtk.main() if __name__ == "__main__": window = MainWindow() window.main()
然后,连接事件。把这个类的 expose-event 连接到一个回调函数 on_expose 上去,expose-event 是一个“曝光”事件,在控件被显示的时候会触发此事件。
class CairoDraw(gtk.DrawingArea): """Drawing with Cairo""" def __init__(self): super(self.__class__,self).__init__() self.connect("expose-event", self.on_expose)
下面来写 on_expose 函数。首先我们要取得我们的 Cairo Context(可以理解为“画板”),这个画板是从 DrawingArea 中的 GdkWindow 中取得的。
def on_expose(self, widget, event): context = widget.window.cairo_create() return False
我们需要设定 DrawingArea 的刷新区域。这样以后每次触发 expose-event 的时候,都会重绘这个区域。
def on_expose(self, widget, event): context = widget.window.cairo_create() context.rectangle (event.area.x, event.area.y, event.area.width, event.area.height) context.clip() return False 我们把绘图的部分写在 draw 这个函数中,然后让控件每秒钟重绘一次。这样每秒时间改变的时候,图形就会刷新,指针就会动了。
def on_expose(self, widget, event): context = widget.window.cairo_create() context.rectangle (event.area.x, event.area.y, event.area.width, event.area.height) context.clip() self.draw(context) glib.timeout_add(1000, self.queue_draw) return False
下面来写 draw 这个函数。
首先来画表盘,也就是一个圆。我们用 arc 函数来画一个弧线,这个弧线的角度从 0 到 2 * pi,也就是一个圆了。注意,这时候我们还没有上色,所以画板依然是空的。使用 set_source_rgb 来设定画笔颜色,fill_preserve 和 stroke 分别用于填充和描边。
def draw(self, context): rect = self.get_allocation() x = rect.x + rect.width / 2 y = rect.x + rect.height / 2 radius = min(rect.width / 2, rect.height / 2) - 5 context.arc(x, y, radius, 0, 2 * math.pi) context.set_source_rgb(1, 1, 1) context.fill_preserve() context.set_source_rgb(0, 0, 0) context.stroke()
接下来画刻度。很简单的算法,值得注意的是 save 和 restore 两个函数,这是一种类似堆栈的保存 Context 状态的方法,这样对线的长度修改之后,很容易就可以恢复过来。
for i in xrange(12): context.save() if i % 3 == 0: inset = .2 * radius else: inset = .1 * radius line_width = context.get_line_width() context.set_line_width(.5 * line_width) context.move_to( x + (radius - inset) * math.cos(i * math.pi / 6), y + (radius - inset) * math.sin(i * math.pi / 6)) context.line_to( x + radius * math.cos(i * math.pi / 6), y + radius * math.sin(i * math.pi / 6)) context.stroke() context.restore()
最后,对指针进行绘制。和表盘差不多的算法。
tm_hour, tm_min, tm_sec = time.localtime()[3:6] handlist = ( (tm_hour, .3, 12, 1.5), (tm_min, .2, 60, 1), (tm_sec, .1, 60, .5), ) for (hand, inset, num, width) in handlist: context.save() inset *= radius line_width = context.get_line_width() context.set_line_width(width * line_width) context.move_to( x + (radius - inset) * math.sin(2 * hand * math.pi / num), y - (radius - inset) * math.cos(2 * hand * math.pi / num)) context.line_to(x, y) context.stroke() context.restore()
这样,一个简单的时钟程序就做好了。源代码在这里。
参考:Writing a Widget Using Cairo and PyGTK 2.8
PS:晕,写完了才发现这篇文章还有下半部分……







