K.I.S.S. – 简单哲学 Linux, Freedom, Arch, Python, Gtk+, C … Keep It Simple, Stupid!

2八/1010

人生苦短,我用 Genie

这篇文章已经酝酿很久了,不过一直没时间没心情写。今天到学校开始学日语了,又看到 WowUbuntu 介绍的日语 50 音图测试软件 Kanatest 在 Arch 的 AUR 里面找不到,所以干脆自己写一个,顺便补上这篇文章。

进入正题,开发一个 Linux 下的软件,你选择的编程语言是什么呢?我想大多数人会选择 C 或者 吧。像上面说的 50 音图测试软件,用 C 开发有些大材小用了,用 呢,要是涉及运算的时候速度又不尽人意。

那么用什么呢?Google 的 Go 语言?库还不完善呢。?有些人会想到这个 GNOME 的项目了,确实, 对于熟悉 C# 的朋友可能十分顺手,可是对于更加喜欢 的朋友,或许就不是那么友好了。

好的,我们的主角登场了,Genie!

既然你知道 ,或许你也知道,它只是对 C 语言的一个包装,运行效率与 C 相差无几。Genie 语言就是 的同胞弟弟,它也是对 C 语言的包装和翻译,能够实现 的所有特性,甚至“编译器”都是相同的——valac!唯一不同的是,Genie 是以 Boo、 等脚本语言为蓝本,语法上更加灵活、简洁,如果你学过 ,Genie 的入门将十分之快!

来看看我写的这个简单的 50 音图背诵软件吧,功能很简单,就是将日语的平假名随机闪现,相信有 + 基础的朋友很容易就可以看懂。

/* Build with valac --pkg +-2.0 mykana.gs */
[indent=4]
uses
    

init
    .init(ref args)
    var window = new MainWindow()
    window.show_all()
    .main()

class MainWindow: Window
    label: Label
    const chars : array of string = {"あ", "い", "う", "え", "お", "か", "き", "く", "け", "こ", "さ", "し", "す", "せ", "そ", "た", "ち", "ち", "て", "と", "な", "に", "ぬ", "ね", "の", "は", "ひ", "ふ", "へ", "ほ", "ま", "み", "む", "め", "も", "や", "う", "よ", "ら", "り", "る", "れ", "ろ", "わ", "を", "ん"}

    init
        title = "My Kana"
        window_position = WindowPosition.CENTER
        default_width = 250
        default_height = 250
        destroy.connect(.main_quit)
        label = new Label(null)
        add(label)
        Timeout.add_seconds(1, this.change_char)

    def change_char(): bool
        var i = Random.int_range(0, 46)
        var output = Markup.printf_escaped("%s", chars[i])
        label.set_markup(output)
        return true

可以看出,除了类型声明之外,从某种程度上讲,Genie 的语法甚至比 更加简单。而且,通过使用 libgee,Genie 甚至可以使用等同于 中的 list、dict 等类型,功能十分强大。Genie 与 共用 API,所以库的问题也不用担心。

不过,Genie 创造于 2008 年,年纪还很轻,文档还不够全。就如同 一样,Genie 的语法也在发展之中。如果你觉得这些都不是问题,那么赶快看看文档学习一下吧!

我觉得很不错的一些文档:官方指南来自 Puppy Linux 的指南官方代码示例

标签: , , , 10 评论
16七/105

GtkApplication – GTK+ 3 中的 Application 支持

+ 3 在紧锣密鼓的开发之中,虽然说 3.0 版本相对于 + 2 在编程方面的改变并不大,不过仍然有些改变是和 + 使用者密切相关的,比如新加入的 GtkApplication 类。

大家知道开始学习 + 的一个难点就是莫名其妙的 gtk_init()、gtk_main(),以及 quit、destroy 等信号的区别之类的,简单来说,一个 Hello World 程序的构建过程很让人困惑,感觉就像记住了一个模板,每次都要写一次。

+ 3 为了解决这个问题,抽象出了 GtkApplication 这个类,那么一切都变得容易理解起来:gtk_init() 就是 GtkApplication 的构造函数,现在只需要调用 gtk_application_new() 就可以,gtk_main() 现在变成了 gtk_application_run(),意义很明显,而程序的退出也只需要连接 GtkApplication 类的 quit 信号即可。

GtkApplication 默认自带一个 GtkWindow,可以通过 gtk_application_get_window() 获得,因此一个新的 Hello World 看起来可能是这样:

#include 
int
main (int argc, char **argv)
{
  GtkApplication *app;
  GtkWindow *window;

  app = gtk_application_new ("org..Example", &argc, &argv);

  window = gtk_application_get_window (app);
  gtk_container_add (GTK_CONTAINER (window), gtk_label_new ("Hello world"));
  gtk_widget_show_all (GTK_WIDGET (window));

  gtk_application_run (app);

  return 0;
}

怎么样,是不是简单明了得多?

更多关于 GtkApplication 类的信息可以参照 GTK+ Reference Manual

标签: 5 评论
8五/1019

神秘软件 Yaner 截图

我就发图,我不说话。

yaner-main.png
yaner-normal.png
yaner-bt.png
yaner-metalink.png

8五/100

简单实现 Python 有序字典(Ordered Dict)

的 Dict 类型很好用,不过有一点可惜就是它的 keys() 是乱序的,想要用它来保存有序的 key-value 对(比如配置文件)就比较困难,碰巧我的毕设就要用到这样一个类型来存放配置文件,怎么办呢?
搜索了一下, 2.7 / 3.1 才有 Ordered Dict 的支持,我总不能去 源码里面拽吧……
还有些方法比较陈旧,继承自 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 许可。

27五/090

在自己的 GTK+ 程序中加入 RGBA 透明支持

上次介绍了怎么在 Arch Linux 下启用 RGBA 透明窗口 ,然而,目前支持 RGBA 的程序还很少,看起来有的透明,有的不透明,很不爽吧?
另外,+ 默认是不启用 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 -- 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(.Window):

     def __init__(self, Context, pluginPage=None, categoryName=None):
         .Window.__init__(self)
+        self.gtk_screen = self.get_screen()
+        colormap = self.gtk_screen.get_rgba_colormap()
+        if colormap:
+            .widget_set_default_colormap(colormap)
         self.ShowingPlugin = None
         self.Context = Context
         self.connect("destroy", self.Quit)
@@ -99,6 +103,7 @@ class MainWin(.Window):
             self.ToggleCategory(None, categoryName)

     def Quit(self, *args):
+        .widget_pop_colormap()
         .main_quit()

     def ResetMainWidgets(self):
--
1.5.2.5

呵呵,这是 CCSM 的 RGBA 补丁,看到了吧,其实关键的也就那么几行而已,在初始化的时候试着载入 RGBA 颜色表,在退出的时候对颜色表进行出栈,其他语言也是这个道理。
有了这几行代码,是不是想把桌面上的所有 + 程序 hack 掉?
呵呵,赶快在自己的程序里加入 RGBA 支持吧!

16五/093

Python 版 Linux 下的迅雷

Linux 下该不该有迅雷,这个问题一直存在分歧,在此也不予讨论。不过,迅雷抗死链的作用是巨大的,这点是不容置疑的,很多人确实用得着。
有需求就有市场,于是乎,Ubuntu 中文论坛的一位放出了 furl 这个小程序,不但可以解析迅雷的 :// 协议,还可以返回迅雷候选地址, xiooli 大侠更是做出了 Shell 脚本,自动调用 进行下载。
可惜的是,furl 是 32 位闭源程序,所依赖的 lib32-libopenssl2 在 Arch Linux 下面安装不了……
突然想起,前些日子,可可熊大侠不是写过一个 pythunder 么?干嘛不用这个下载呢?于是就有了下面的程序……

#!/usr/bin/env #pyaria2.py

import os, sys, urllib

def usage():    print """Usage:  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()

很简单的一个脚本,呵呵,参数和 是一样的,区别只在于对于 url 的处理(暂时 url 只能放在命令行的最后)。
比如下载 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe,就运行:

 tharia2.py http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe

默认 可以支持 5 线程,如果你想改为 10 线程,那么:

 tharia2.py -s 10 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe

指定下载目录,用 -d:

 tharia2.py -d "/home/iven" -s 10 http://www.dmato.com/DownloadFile/FishDesk2009Beta4.exe

更多用法详见:

aria2c --help

目前的主要问题是,可可熊大侠的网站相应速度太慢了,过半分钟才会返回候选列表,汗……不知道是不是我的网速问题,大家可以试一下。另外,就是没有解迅雷的 :// 协议了,还有快车什么的,这个貌似不难,有时间研究一下。
现在还没有开源版本的迅雷候选地址搜索工具,主要大家怕流传太广,遭到迅雷封锁。但是还是好想看看代码是怎么写的啊……
最后,项目的地址:http://github.com/iven/tharia2/

13五/092

Python – 你可能不知道的

最近决定系统学习 ,于是把去年 ChinaUnix 的赠书《 核心编程》掏出来,使劲啃……
通过这几天的学习,总算是看完了前六章,发现许多以前在《Dive Into 》里面没有提到,或者很少提到,或者很容易忘掉的特性,在此记录一下~呵呵,感谢 CU 的赞助,感谢 CCAV,感谢 GFW,感谢 宋吉广 的人品~
* 首先,交换 x, y 的值:

x, y = y, x

* 跟 C 不同,连续的判断:

if x > y > z:

* 跨平台,平台自适应的换行符(\n、\r\n……):

os.linesep

* `foo`的作用和 repr(foo) 的作用是一样的(虽然``已经不推荐使用)
* 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() 可以用来做深拷贝

还有很多不知道的啊,继续学习……

标签: 2 评论
2五/090

使用 PyGTK 和 Cairo 编写一个简单的时钟

呵呵,其实说这个是个自定义控件也不为过。
先介绍一下 Cairo:Cairo 是一套提供设备独立的矢量图形 API 的二维图形库,支持很多不同的后端,如果可能的话,还可以使用硬件加速绘图。Cairo 是用 C 语言编写的,但是提供了很多其他语言的绑定,Factor, Haskell, Lua, Perl, , Ruby, Scheme, Smalltalk 等等。Cairo 基于 LGPL 和 MPL 双许可,这意味着你可以用它开发闭源软件。Cairo 是自由软件。
关于 Cairo 的更多详情,可以参照维基百科
Cairo 的使用方法很简单,跟中学时学习的 LOGO 语言差不多,这里用 + 编写一个时钟出来。
首先,建立一个窗口。+ 提供了一个专门用来画图的控件 DrawingArea,我们继承它,建立一个类 CairoDrwa,并且把一个对象加到窗口上去。虽然 + 本身提供了不少绘图功能,不过这里只说 Cairo。

import , glib, math, time
class CairoDraw(.DrawingArea):
   """Drawing with Cairo"""
   def __init__(self):
       super(self.__class__,self).__init__()

class MainWindow(.Window):
   """Main window of the test program"""
   def __init__(self):
       super(self.__class__, self).__init__(.WINDOW_TOPLEVEL)
       self.set_title("Cairo test")
       self.connect("delete-event", .main_quit)

       cairo_test= CairoDraw()
       self.add(cairo_test)

   def main(self):
       self.show_all()
       .main()

if __name__ == "__main__":
   window = MainWindow()
   window.main()

然后,连接事件。把这个类的 expose-event 连接到一个回调函数 on_expose 上去,expose-event 是一个“曝光”事件,在控件被显示的时候会触发此事件。

class CairoDraw(.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 分别用于填充和描边。

clock1.png

    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 状态的方法,这样对线的长度修改之后,很容易就可以恢复过来。

clock2.png

        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()

最后,对指针进行绘制。和表盘差不多的算法。

clock3.png

        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:晕,写完了才发现这篇文章还有下半部分……

30四/090

使用 gitk 跟踪 git 项目源代码

看源代码是学习编程最快、最有效的方法之一,在 Linux 开源的大环境下,研究源代码就更加容易了。面对一个大一点的项目,我们可能无从下手,那么,何不从其雏形阶段开始研究呢?
我现在就在研究 Tualatrix 兄的 ubuntu-tweak 的源代码,以前 ubuntu-tweak 托管于 Google Code 的时候,Google Code 对各版本差异分析得很清楚,然而转向 github 之后,限于网速,看起来很不方便。
有什么跟踪本地 项目源代码的工具呢?diff 显然不是最好的方法,尤其涉及到文件移位之类的问题时。
Tualatrix 兄用的是 gitg,可是我用起来总是感觉缺点什么的样子,而且经常会莫名其妙的崩溃。偶然看 官方文档时,认识了 ,似乎是官方提供的工具,用起来真是越来越顺手。
先看一下两个软件的对比图:

gitg.pnggitk.png

基于 tk/tcl,所以用之前要安装这两个包,由于是官方软件,安装 时就会自动安装,不必另行安装。

从图中可以看出,基于 + 的 界面更加漂亮,代码差异的彩色背景更加显眼,相比之下,基于 tk 的 显然简陋许多,大概 的作者也就是想写个 + 版本吧。
不过,仔细看的话,就可以看出很多不同来,比如,中间的横条空间, 是用作显示 Commit 的详情,这显然是浪费了宝贵的屏幕空间,尤其对于宽屏用户来说。

相对地, 虽然中间的空间也用了,不过放置的却是实用的搜索功能。千万不要小看这个搜索条,它不但可以搜索 Headline、Comments、Author、Committer,还可以搜索哪些 Commit 对某些文件进行了修改,并且支持正则表达式、大小写匹配。

再来看一下 diff 功能, 也比 强上很多,不但可以选择查看差异、新版、旧版的代码,还支持忽略空白符,设定 diff 周边行数。不要小看这个功能,在查看源代码的时候,这个功能对于理解上下文是很重要的。

如果这么强大的差异功能你还不满意,或者觉得视野空间太小的话,还可以右击文件,选择 External diff 来调用 meld 查看,当然前提是你安装了 meld。

右击文件,还可以对该文件加上高亮,这样标记已经读懂的文件比较方便。

通过这些介绍,可以看出,相比 更加成熟,功能更加强大。

默认启动 是打开 master 分支,如果想要查看其他分支,使用 分支名 即可,比如:

 origin

的命令行还有很多用法,详见 相关文档。

其实, 只是 gui 中的一个小工具,在命令行输入:

 gui

可以发现更多功能,当然,有些功能就不如终端下方便了。下面是 gui 的效果:

git-gui.png

27四/090

关于 C++ 中模板类(Template Class)在多文件中的问题

最近 C++ 学到模板类了,老师要做一个模板类的题目。
一直以来我们都是保持着类声明和类实现分别写在头文件和 CPP 文件中这个不错的习惯,这次也没有例外,然而这次问题却出来了。
先是在 G++ 中提示:

undefined reference to `Array::Array(int, int const*)'

移到 VC++ 6.0 后又提示:

ex_08_1.obj : error LNK2001: unresolved external symbol "public: __thiscall Array::~Array(void)" (??1?$Array@H@@QAE@XZ)
ex_08_1.obj : error LNK2001: unresolved external symbol "public: __thiscall Array::Array(int,int const *)" (??0?$Array@H@@QAE@HPBH@Z)

到底是怎么回事呢?老师说是编译器的 BUG,我却觉得没那么简单。
搜索了一下,才发现,C++ 中模板类(或者叫“类模板”)的声明和实现和普通类是不同的,模板类的声明有两种模式:包含编译模式和分离编译模式。
包含编译模式就是把声明和实现写在同一个文件中;分离编译模式当然就是写在不同文件中,不过用这种形式的时候,要在类的声明前加上 export 关键字。
我在 G++ 上试了一下,出现:

警告:关键字‘export’未实现,将被忽略

汗一个……VC++ 上更不用说了……
C++ 真是个复杂的东西,还是写在一个文件里吧……

标签: 没有评论
   下一页