网络寻租

Programmer, Gamer, Hacker

Pyside介绍

| Comments

什么是pyside?

`pyside`_ 是一个Qt4在python下面的绑定, 是PyQt4的取代. 它和PyQt4不同的地方是, 支持商业闭源应用, 以及是Qt4官方支持的.

pyside现在状况怎么样?

第一个beta版本已于2010/12/26释放. 处于火热开发中. pyside是完全跟进qt的. beta里面带有qml的支持.

如何获取pyside?

如果你用ubuntu, 加一下ppa的源就好了. 网址: https://launchpad.net/\~pyside/+archive/ppa

执行命令:

也可以去clone `pyside源码库`_.

尝试pyside

我们通过几个示例来看pyside的状况:

简单示例

一个简单的示例, pyside提供和pyqt API级别的互通, 方便pyqt用户迁移. 与pyqt代码的区别, 只在import的时候把PyQt4改成PySide就好了:

UI

看起来写代码还OK, 我们看一些其他的功能, 比如如何支持ui. 我们用qtdesigner写好了ui文件, 如何在pyside下面使用?

pyqt有几种方式:

  • 采用pyuic4把ui文件转换为python代码. pyside也有一个这样的工具, 名称是pyside-uic.(还有pyside-rcc, pyside-lupdate与pyqt对应)

  • 代码中动态导入, pyqt的代码是这样写的:

1
2
3
4
5
form, base = uic.loadUiType("score.ui")
class ScoreDlg(QDialog, form):
    def __init__(self):
        super(ScoreDlg, self).__init__()
        self.setupUi(self)

在pyside里面, 直接生成一个对象的方法:

1
2
3
4
from PySide.QtUiTools import QUiLoader
loader = QUiLoader()
widget = loader.load('mywidget.ui')
widget.show()

我上pyside的maillist, 找到了动态生成的方式, 把代码放在这里吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyQUiLoader(QUiLoader):
   def __init__(self, baseinstance):
       super(MyQUiLoader, self).__init__()
       self.baseinstance = baseinstance

   def createWidget(self, className, parent=None, name=""):
       widget = QUiLoader.createWidget(self, className, parent, name)
       if parent is None:
           return self.baseinstance
       else:
           setattr(self.baseinstance, name, widget)
           return widget

def loadUi(uifile, baseinstance=None):
   loader = MyQUiLoader(baseinstance)
   ui = loader.load(uifile)
   QMetaObject.connectSlotsByName(ui)
   return ui

然后, 我们就可以用loadUi来扩展一个类了:

1
2
3
4
5
6
class Inputer(QDialog):
    def __init__(self):
        super(Inputer, self).__init__()
        loadUi('draw.ui', self)
        self.leInput.returnPressed.connect(self.input)
        ...

QML, QML!

pyside可以和qml一起使用, 不过我对qml不熟悉, 等以后熟悉了再来修改具体的示例吧. 你可以直接看 `pyside示例代码`_ 里面关于declarative的部分.

结论

经过测试, pyside现在大致可用, 不过在一些地方会有bug, 现在是beta版本, 根据这个态势, 很快就能稳定了. 如果因为pyqt价格问题观望的同学, 现在可以下手pyside了, 也可以当当小白鼠, 为开源社区做点贡献(从我做起吧).

资源

简单python_宣告式编程

| Comments

什么是宣告式编程?

image

我们编程的时候, 往往是实现某个领域的业务逻辑, 而这些业务逻辑, 往往可以用定义的方式来表述. 程序员需要做的工作是, 如何把把这些业务逻辑的表述(信息), 用写程序的方式又好又快地实现出来. 如果你是一个资深程序员, 你会发现, 自己会从: 根据需求, 命令式地写代码, 控制机器一个动作又一个动作; 慢慢转移到: 用合适的文本描述需求, 然后想办法写个简单的解释器, 把这个文本转换为可执行的程序. 这种开发程序的方式, 我们叫它 宣告式編程.

大多数情况下, 我们会受到工具的限制, 不能很自然地完成这样的一个转换工作, 如果你有遇到这样的问题, 我建议你, 使用python作为编程语言.

为什么用python?

为了能够很好地支持宣告式編程, 我们需要满足几点:

  • 编程语言能够方便地完成数据结构的定义, 并能嵌入在代码当中.

    python内嵌有list, dict, set等数据结构, 并且能够把它们组合起来, 搭建更复杂的结构. 而它们的定义, 是非常简单和清晰的:

1
2
3
4
data = dict(
    name = 'me',
    address = ['shanghai', 'chengdu'],
)
  • 也能够很方便地访问和修改这些数据结构.

    python是动态语言, 不需要用户去具体处理资源分配的问题, python会帮助处理这些琐碎的事情.

1
2
data['name'] = 'halida'
data['address'].append('kunshang')
  • 并且能够根据这些数据结构动态地完成一些具体的操作.

    python具有很高的动态特性, 能够在运行时动态生成, 修改对象的特性:

1
2
3
4
5
6
7
8
class P(): pass
p = P()
p.v # 错误! p没有v

# 我们给它设置一个:
P.v = 12

p.v # --> 12

我们具体去看一个示例吧.

示例: 配置界面

我们需要写一个简单的配置用户界面, 用户可以设置一些配置功能. 我们一开始的考虑是, 完全手写, 或者采用WYSIWYG工具, 拖拖拉拉把界面整理出来, 然后再写一个set/get的方法. 比方说, 这样写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ConfigDlg(QDialog):
    def init(self):
         self.leName = QLineEdit()
         self.sbValue = QSpinBox()
         ...

    def setUi(self, data):
         self.leName.setText(data['name'])
         self.sbValue.setValue(data['value'])
         ...

    def getUi(self):
         data = {}
         data['name'] = self.leName.text()
         data['value'] = self.sbValue.value()
         ...
         return data

但是如果需求变动了怎么办? 如果配置需要添加, 修改? 那么我们就再去修改程序或者代码. 问题是, 程序写了有一段时间了, 变得陌生起来, 除非通读一遍, 不然很容易修改错. 并且代码有点散乱, 就像上面的代码, 在setUi里面写了逻辑, 如果在getUi里面忘记写了就麻烦了.

pythonic的一个原则: Simple is better than complex. 上面写代码的方式不能反映问题的本质, 我们需求的本质信息, 是我们需要配置什么内容, 以及这些内容应该用什么方式表现出来. 在上面的例子里面, 就是name和value. 我们用python数据结构的方式, 重新整理一遍需要配置的内容:

1
2
3
4
CONFIG = (
    (unicode, 'name'),
    (int, 'value'),
)

这样是不是很清晰?

我们还要定义类型对应的控件, 以及具体处理数据的方法:

1
2
3
4
TYPES = dict(
    unicode: (QLineEdit, 'setText', 'text'),
    int: (QSpinBox, 'setValue', 'value'),
)

现在我们需要把这样的配置转换为具体的界面, 下面就是具体转换的代码, 用命令式就很直截了当了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ConfigDlg(QDialog):
    def init(self):
        self.widgets = {}
        for type, name in CONFIG:
        self.widgets[name] = TYPES[type][0], type

    def setUi(self, data):
        for key, value in data.iteritems():
            w, type = self.widgets(key)
            getattr(w, types[type][1])(value)

    def getUi(self):
        data = {}
        for key, value in self.widgets.iteritems():
            w, type = value
            data[key] = getattr(w, types[type][2])()
        return data

要注意到, 我们能够这样’宣告式’地写设置内容, 有几个前提:

  • 设置部分的信息是属于变动的信息

    我们实现一个宣告式的平台付出的代价能够被需求变动挽回的时间损失所弥补. 如果设置永远都不需要去改, 我们就没有必要这样设计了. 在上面的例子对应的真实需求里面, 开始的时候也是用最上面的方法来完成. 然后发生了需求变更, 再改成了后面宣告的方式. 这里也反映了python的一个优点: 代码足够少, 足够清晰, 能够非常方便和快速地重构掉.

  • 基础是不变的

    在上面的例子里面, 我们默认了一些前提: 这个设置见面是在一个QDialog里面, 如果要求配置是分类, 分多个QDialog怎么办? 这里我们需要进行一下预估, 什么信息是’泰山’一样永久不变, 什么信息是’羽毛’一样飘忽不定… 这个需要程序员对业务足够熟悉.

结论

当我们需要实现一个功能的时候, 首先去探索, 这个功能的本质信息是那些? 这样的信息中, 那些是固定的? 哪些是变动的? 然后我们根据信息的本质属性, 选择对应的处理方式. 如果信息是经常变动的, 而它们变动的范围也是可以包括在一个问题域里面, 我们可以用宣告式编程, 来把他们描述出来. 不过, 如果这个问题域足够大, 足够精深, 我们恐怕要考虑专门做一门语言来包容它们了.

Scons介绍

| Comments

什么是scons?

image scons不是上面的小甜饼, 而是一个基于python的自动化构建工具. 和make一个性质. 不过, 更高级一些.

为什么要用它呢?

  • 高阶的make工具

    make是不错, 但是太过简单了, 很多东西都要重复写, 很多事情都做不了.. 我们需要一个更高级的工具.

  • python

    既然我们要用更高级的工具, 去找找看: automake, cmake, qmake, ant.. 已经有一堆东西了, 我们选择哪一个呢?

    按照我的看法, 对于任何复杂的工具, 本质上来说, 都需要一个足够强大的编程语言来支持, 以便实现自动化和高可配置性. 既然python已经成为我的”main stream”语言, 当然要看支持python的工具了. google python + make, 第一个结果就是scons了. 当然, 还有很多其他的 pyghon构建工具 可以选择.

安装

  • debian源:
sudo apt-get install scons
  • 或者直接在安装了python的环境里面easy_install:
eays_install scons

对了, 如果在windows下面, easy_install安装后有可能出现: import error: 找不到Scons.Script. 我研究了一下, 发现在scons放的位置不对, 只要搜索下把scons的目录放到dist-packages文件夹里面就好了.

一个简单的示例

我们直接去看一个示例吧, 目标是编译一个hello world c程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 新建一个目录
rm t1 -rf; mkdir t1; cd t1

# 这里是我们的程序    
cat > hello.c <<EOF
#include <stdio.h>
int main(){
    printf("hello, world!\n");
    return 0;
}
EOF

# scons的脚本文件名称是SConstruct/Sconstruct/sconstruct, 如果直接执行scons, 会按照上面的顺序找文件. 和make类似.
cat > sconstruct <<EOF
# sconstruct其实就是一个python脚本, 支持所有python能做的事情.
BIN = 'hello'
# scons和其他构建工具一样, 是定义式的, 我们定义需要构建一个程序, 名称是BIN, 依赖hello.c
Program(BIN, ['hello.c'])
# 然后, 我们再定义一个执行的命令, 方便看结果. 它依赖BIN, 方法就是直接执行这个程序.
Command('run', BIN, './'+BIN)
EOF

# 最后, 我们调用scons来执行run的命令.    
scons run

这里是结果:

1
2
3
4
5
6
7
8
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
gcc -o hello.o -c hello.c
gcc -o hello hello.o
./hello
hello, world!
scons: done building targets.

scons会自动根据文件来调用对应的构建工具.

上面只是一个示例, 更多的最好去看 `scons官方文档`_. 文档有了我就不需要再写一遍了.

我自己整理的一些重点

我们可以构建:

  • Program
  • Object
  • Library
  • SharedLibrary

批量获取源文件:

1
Program('hello', Glob('*.c'))

设置参数: LIBS, LIBPATH, CCFLAGS, CPPPATH

我们可以设置一个环境:

1
2
3
env = Environment(CC = 'gcc',
                  CCFLAGS = '-O2')
env.Program('hello.c')

可以设置判断是否修改的算法:

1
2
3
4
5
6
7
8
# 默认采用算MD5的方法判断文件是否修改
Decider('MD5')
# 可以设置传统的看timestamp是不是最新的方式
Decider('timestamp-newer'/make) #
# 也可以设置只要timestamp变了就算文件被修改了
Decider('timestamp-match')
# 混合: timestamp改变了, MD5也变了才算修改了
Decider('MD5-timestamp')

可以根据Enviroment设置Decider

缓存判断依赖关系

1
SetOption('implicit_cache', 1)

设置依赖关系

1
2
Depends(hello, 'otherfile')
Ignore(hello_obj, 'hello.h')

我最喜欢的, 执行自定义的命令:

1
2
3
Command('hello.o', 'hello.c',
        ['gcc $SOURCE -c',
         'wc -l $SOURCE >> summary'])

结论

看起来scons还是有点意思的, 我先用一段时间, 等有了一定的感觉之后再来看看.

资源

我的emacs配置

| Comments

每个emacs使用者的配置都是不一样的, 都会根据自己的习惯和喜好来调整. 这里我整理一些对emacs用户调整有帮助的内容, 以及我自己觉得不错的配置.

emacs配置如何管理?

配置多了, 放在.emacs里面不是一回事, 我的解法是, .emacs导入真正放配置的目录, 然后require:

1
2
(add-to-list 'load-path (expand-file-name "~/Dropbox/sync/emacs/srcs"))
(load "main")

main.el是配置的汇总, 配置按照模块和功能分割成不同的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
(provide 'main)

;; requires
(require 'mylib)
(require 'template)
(require 'mypython)
(require 'mydired)
(require 'myc)
(require 'mykeymap)
(require 'mygtd)
(require 'top-mode)
(require 'tabbar)
(require 'fullscreen)

dropbox是我同步emacs配置的工具, 你也可以用其他网盘工具.

如何绑定自己的键位?

每个emacs用户都会自己设定键位, 不会傻傻地M-x然后输入字符. 我个人的经验, 绑定键位的指导思想是: 对于显示buffer, 切换buffer之类使用非常频繁的功能, 找一些方便的直接键位来绑定, 比如:

1
(global-set-key [C-tab] 'other-window)

对于一些其他不是那么频繁, 但是经常要使用的功能, 都可以绑定到间接键位上面. 我发现C-;没有使用, 按起来还很方便, 然后把所有自己定义的功能都加到上面去了:

1
2
3
4
5
6
7
(global-set-key (kbd "C-; 1") 'gtd)
(global-set-key (kbd "C-; 2") 'note)
(global-set-key (kbd "C-; 3") 'scratch)
(global-set-key (kbd "C-; 4") 'py-shell)
(global-set-key (kbd "C-; 5") 'woman)
(global-set-key (kbd "C-; 7") 'twit-follow-recent-tweets)
(global-set-key (kbd "C-; C-7") 'twit-post)

对于针对某些mode的键位绑定, 只需要改变那个mode的键位就可以了:

1
(define-key dired-mode-map (kbd "b") 'dired-up-directory)

一定要设置的功能

有些功能是如此的强大, 以至于缺少了他们emacs就不完整了..

  • ido, 如果你发现开启文件和切换buffer输入有点累的话, 用ido来节省输入.

  • auto-complete 自动补全. 这里面的补全方案默认是取当前buffer的词语的, 对于我写python代码来说足够用了. 至于c/c++/java..等我写的时候再配置吧.

1
2
3
4
5
(require 'auto-complete)
(global-auto-complete-mode t)
;; 匹配项目不用动手腕啦
(define-key ac-complete-mode-map "\C-n" 'ac-next)
(define-key ac-complete-mode-map "\C-p" 'ac-previous)
  • 漂亮的配色. color-theme有一堆配色可以选的.
1
2
(require 'color-theme)
(color-theme-arjen)

还有一些零散的配置, 都放在 main.el 里面.

  • template 模板系统. 写东西怎么不能用模板呢. 一遍遍地输入同样的东西多烦. 我用的是 template.el, 不过感觉用的不是很熟, 还要改改.

  • org-mode. 用来写notes/todo/gtd的. 看看吧, 功能强大.

其他有价值的东西

  • shell-command功能. 可以在buffer当前目录执行命令. 如果不想它卡死的话, 可以在命令后面加&(linux).
  • artist-mode, 可以在emacs里面画图. artist-mode视频介绍
  • 半透明. linux下面要开启特效才能半透明.
1
2
(set-frame-parameter (selected-frame) 'alpha '(80 50))
(add-to-list 'default-frame-alist '(alpha 80 50))
  • 全屏, 不全屏怎么对得起emacs呢?

资源

emacs自带的info文档已经很多了, 不过有一些其他地方的资源不错, 值得提一下:

Ipython

| Comments

什么是ipython

python里面的一个杀手级应用就是它的解释器, 我们可以利用这个解释器来完成一些日常的工作, 比如计算器, 字符串处理, 其他计算显示什么的. 但是python本身自带的解释器功能不够强大, 所以有了 ipython 这个大杀器. 上面的图片显示的就是ipython的具体效果.

ipython的目标是成为一个全方位的交互性和探索计算的平台, 它好像是隶属于scipy下面的, 莫非有替代matlab的期望? 对于我们普通人来说, 它的特性已经足够好到替换掉python自带的那个解释器了. 下面为大家介绍.

如何安装

先把ipython装起来吧. ubuntu下面: .. code-block:: sh

sudo apt-get install ipython

其他系统可以看 安装文档

ipython好在哪里?

列出一些我觉得好用的东西:

  • 自动补全:

    按下tab键会自动补全当前输入的对象名称.比如:

In [1]: os.<tab> # 会自动列出os下面的所有东西
In [2]: abcd = 12
In [3]: abc<tab> # 自动补全成abcd
  • 缓存输入输出:

    提示符是In [n]: 的格式, 也就是说, 你可以访问In和Out这2个list, 获取输入和输出.

  • 整合命令行工具:

    可以用!ls的方式来执行命令行工具, 甚至可以把结果当作参数来使用:

In [4]: out = !ls
In [5]: for file in out: print file

bin
temp
  • 省略括号
In [6]: s = "hello"
In [7]: s.replace 'he', 'yi'

'yillo'
  • 交互式GUI操作

    ipython内嵌GUI处理机制, 使用tk/wxpython/pyqt可以交互式地创建和修改类. 创建出的对象是可以响应用户输入的, 并且能够实时修改.

In [8]: from PyQt4.QtGui import *
In [9]: dlg = QDialog(); dlg.show()
In [10]: le = QLineEdit(); lw = QListWidget(); 
In [11]: l = QVBoxLayout(dlg); l.addWidget(le); l.addWidget(lw)
  • 帮助系统

    对于任何对象s, 都可以用s??, s?的方式来看它的帮助文档.

  • run

    可以用

In[12]: run xxx.py

来执行代码.

  • sh模式

    ipython可以当作shell来使用. 只要执行:

1
ipython -p sh

具体说明见: http://ipython.scipy.org/doc/stable/html/interactive/shell.html

还有其他的很多很多很多特性, 我不一一列出来了.. 具体可以看 overview, 以及进入ipython后, 运行:

%quickref

来获取介绍信息.

emacs + ipython

作为emacs控, 所有东西都要整合到emacs里面去. 根据 emacs python文档 , 我只设置了这几行代码:

1
2
(require 'ipython)
(global-set-key (kbd "C-; 4") 'py-shell)

不过, 我还装了几个其他东西(ubuntu下面):

  • ipython
  • python-mode
  • emacs23

安装ipython后, 会自动给你一个配置文件:

/usr/share/emacs/site-lisp/ipython.el 

所以只要require它就可以了.

相关资源

  • `ipython`_ 官方网站
  • 文档

Pyqt_animation_framework

| Comments

Qt animation framework介绍

传统的GUI界面, 一般是静态的, 按钮就是按钮, 不会到处乱蹦, 不过现在这个时代, 冷冰冰的界面不是很吸引人. Qt 4.6里面带有新的animation framework, 就可以帮助我们完成让界面”动起来”的工作.

系统架构

image 上面是Qt支持动画的所有类的继承关系列表, 最主要的有几个:

  • QPropertyAnimation 类负责具体的动画效果, 它可以通过设置QWidget属性(property)的方式来完成动画.

  • QAnimationGroup, 用来把不同的动画拼在一起, 实现连续和复杂的动画效果.

具体我们看实际的代码吧.

示例1: 一个简单的动画

正如上面所说的, `QPropertyAnimation`_ 负责动画, 这里一个简单的示例, 控制一个按钮移动. 代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 动来动去的按钮
button = QPushButton("Button")
button.show()

# 生成一个动画, 它会修改button的geometry属性
animation = QPropertyAnimation(button, "geometry")
# 动画时间是10秒
animation.setDuration(10000)
# 开始的位置
animation.setStartValue(QRect(0, 0, 100, 30))
# 结束的位置
animation.setEndValue(QRect(250, 250, 100, 30))
# 开始吧
animation.start()

很简单吧? 它默认是以恒定速度运动的, 可以通过设置 easyCurve 来设置运动的类型, 比如先加速后减速什么的, 很方便.

示例2: 分组

上面只是一个普通的动作, 如果我们需要做一组复杂的动作的话, 就要用 `QAnimationGroup`_ 来组织. 有2种group, QParallelAnimationGroup和QtSequentialAnimationGroup, 一个平行动作, 一个顺序动作. 这个示例设置无数个按钮淡入到窗口中. 上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#生成一堆按钮, 不同的初始位置
self.pbs = []
for i in range(8):
    pb = QPushButton(str(i), self)
    pb.move((i*100) % 600, i*40 + 10)
    self.pbs.append(pb)

# 建立一个平行执行的动作组
ag = QParallelAnimationGroup()
for w in self.pbs:
    # 对于每个按钮, 都生成一个进入的动作
    a = QPropertyAnimation(w, "geometry")
    a.setDuration(1000)
    a.setEasingCurve(QEasingCurve.OutQuad)
    a.setStartValue(QRect(-100, w.y(), w.width(), w.height()))
    a.setEndValue(w.geometry())
    # 添加到组里面去
    ag.addAnimation(a)

ag.start()

其实简单点说, 只要把要动作的animation添加到group里面, 然后start就好了.

示例3: 状态机

Qt本身提供一个状态机的功能, 和animation结合起来, 可以很方便地完成我们想要的工作. 毕竟, 不同状态的切换, 才是界面运动的本质. 这个示例演示一个按钮, 点击后会在两个位置之间移动.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
dlg = QDialog()
dlg.resize(500, 300)
button = QPushButton("Button", dlg)

# 我们先生成一个状态机
machine = QStateMachine()

# 然后给状态机加上几个状态:
# 不同状态下, button的位置是不同的.
state1 =  QState(machine)
state1.assignProperty(button, "geometry", QRect(0, 0, 100, 30))
state2 =  QState(machine)
state2.assignProperty(button, "geometry", QRect(250, 250, 100, 30))
machine.setInitialState(state1) # 初始状态是哪个

# 然后, 我们需要设置状态变化的转换方式.
transition1 = state1.addTransition(button.clicked, state2)
transition2 = state2.addTransition(button.clicked, state1)

# 把动作加到转换方式里面去
an = QPropertyAnimation(button, "geometry")
transition1.addAnimation(an)
an2 = QPropertyAnimation(button, "geometry")
transition2.addAnimation(an2)
# 设置完了, 开始吧.
machine.start()
dlg.exec_()

就是这么简单..

资源

如果你安装了Qt4.6以上版本, 可以在assistant-qt4里面搜索the animation framework看具体的文档, 也可以看 `在线版本 <http://doc.qt.nokia.com/4.7/animation-overview.html>`. 上面的例子都是脱胎于这个文档的.

Qt附带有几个好玩的示例, 在qt example下面的animation目录下面. pyqt也是一样.

上面我写的的几个示例代码, 可以在这里下载到: http://bitbucket.org/linjunhalida/code-example/src/tip/animation/

结论

Qt animation framework已经出来那么久了, 我现在才真正去学习它, 是有点晚了, 后面的QML都出来的时候, 现在再来看是否有点不合时宜? 但是看着Qt演变的过程, 还是有意义的. 有时间的话, 我去看看它实现的原理, 能不能从中借鉴点什么.

PS: pythonxy里面的pyqt还是4.5, 没有带有animation framework, 郁闷. 不好把功能加到公司项目里面去了.

Qwt介绍与使用

| Comments

什么是Qwt?

image Qwt 是一个Qt的第三方库, 它的作用是为了方便一些科学技术相关领域GUI程序的开发. 它提供了一些好的控件: 2D谱图的绘制, 示波器等仪器上面有的控件:比如旋钮什么的.

我使用它, 是因为我要画曲线图, 频谱图等一系列科学领域需要展示的东西. 其实, 任何数据图, 都可以采用Qwt来绘制. 个人觉得它Qt里面最好用的画谱图工具. 对了, 它的性能很好, 可以做实时显示数据.

Qwt官方网站上面没有一步步的教程, 但是API的文档很全, 并且代码库中带有有很多例子可以参考. 因为我现在是采用pyqt做开发, 所以我使用 pyqwt 这个python下面的绑定. 上面也有很多示例. 虽然是python的代码, 我想, C++程序员照样能够看懂(应该说所有的程序员都应该可以看懂..)

Qwt架构

Qwt的架构很简单, 官方文档上面虽然没有说, 但是我整理了一下:

QwtPlot
   |
   |------- QwtPlotCurve
   |
   |------- QwtPlotCurve

QwtPlot就是谱图显示的控件, 任何需要显示的曲线(包括网格什么的), 都是QwtPlotCurve, 一个QwtPlot上面可以放很多的QwtPlotCurve.

具体职责:

  • QwtPlot 负责坐标的部分, 比如显示的范围, X轴Y轴什么的. 要注意的是, 它可以有多个X和多个Y, 方便不同Y值的谱图叠在一起.
  • QwtPlotCurve负责如何去绘制谱图, 比如画直方图, 网格什么的. 它还有一个子类: QwtPlotMarker, 可以对谱图的特定位置做标记.

上代码

下面是我的一个实例代码, 内容就是负责画一个直方图, 效果如下:

image 代码在 这里 .

Qml介绍

| Comments

QML

今天去看Qt的最新进展, 发现: Qt 4.7 已于2010年九月份发布了, 里面带有一个全新的写GUI的方式: QML.

具体相关的介绍, 在 Qt4.7官方文档 上面有 QML介绍 . 我花了半个晚上的时间来学习 QML的教程 , 发现真的不错.

下面是 `QML的教程`_ 里面的内容, 我稍微介绍一下.

示例1

简单点说, QML就是以定义的方式来写界面, 格式很像CSS. 一个简单的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//首先引入相关的模块
import QtQuick 1.0

Rectangle {
    id: page
    width: 500; height: 200
    color: "lightgray"

    Text {
        id: helloText
        text: "Hello world!"
        y: 30
        anchors.horizontalCenter: page.horizontalCenter
        font.pointSize: 24; font.bold: true
    }
}

这里面定义了一个Rectangle(其实就是方块啦)作为背景, 上面再加了一个Text. 注意, Text的位置是通过设定anchors的方法来定位的.

显示的效果就是:

image

示例2

QML能够自己定义对象类型, 比如,新建一个文件Cell.qml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import QtQuick 1.0

Item {
    id: container
    property alias cellColor: rectangle.color
    signal clicked(color cellColor)

    width: 40; height: 25

    Rectangle {
        id: rectangle
        border.color: "white"
        anchors.fill: parent
    }

    MouseArea {
        anchors.fill: parent
        onClicked: container.clicked(container.cellColor)
    }
}

这里面定义了一个Cell类型, 它里面有一个Rectangle, 以及一个MouseArea(后面再说).

我们可以定义新的属性:

1
property alias cellColor: rectangle.color

这个属性只是Rectangle颜色的别名.

也可以定义新的事件:

1
signal clicked(color cellColor)

MouseArea是用来做用户交互的, 我们把它的onClicked事件和我们定义的clicked事件绑定起来:

1
2
3
4
MouseArea {
    ...
    onClicked: container.clicked(container.cellColor)
}

这样, 当用户点击Cell之后, 就会触发clicked事件, 这个事件附带Cell方块的颜色作为参数.

我们定义了一个Cell之后, 如何使用呢? 这里是使用Cell的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import QtQuick 1.0

Rectangle {
    id: page
    width: 500; height: 200
    color: "lightgray"

    Text {
        id: helloText
        text: "Hello world!"
        y: 30
        anchors.horizontalCenter: page.horizontalCenter
        font.pointSize: 24; font.bold: true
    }

    Grid {
        id: colorPicker
        x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
        rows: 2; columns: 3; spacing: 3

        Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
        Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
        Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
        Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
        Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
        Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
    }
}

效果如下:

image 别看上面那些代码那么多, 其实都是些图块的定义. 功能就是, 点击上面一堆Cell其中的一个, 就会改变Hello world的颜色. 如何做到的呢? 只要定义Cell的onClicked事件引发后, 对应执行的代码就好了:

1
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }

示例3

QML最神奇的地方是, 能够定义效果, 比如实现这个:

image 当鼠标点击hello world的时候, 就会出现上面的动画.

核心代码是这个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Text {
    text: "Hello world!"
    ...

    MouseArea { id: mouseArea; anchors.fill: parent }

    states: State {
        name: "down"; when: mouseArea.pressed == true
        PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
    }

    transitions: Transition {
        from: ""; to: "down"; reversible: true
        ParallelAnimation {
            NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }
            ColorAnimation { duration: 500 }
        }
    }
}

先定义一个MouseArea, 捕捉用户点击的事件

1
MouseArea { id: mouseArea; anchors.fill: parent }

然后定义一个Hello world翻转的状态, 当mouseArea被点击的时候, 就会把自己变成翻转的状态.

1
2
3
4
states: State {
    name: "down"; when: mouseArea.pressed == true
    PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}

当然, 现在的话点击之后, 就会立刻把hello world翻转过来, 我们要定义一个效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
transitions: Transition {
    // 这个效果是为从初始状态变为down状态而设定的
    from: ""; to: "down";

// 并且这个效果是双向的, down变回初始状态也会反过来执行这样的效果.
    reversible: true

// 好了, 这里具体定义效果是什么. ParallelAnimation是说同时进行若干效果
    ParallelAnimation {
    // 转换方向
        NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }

        // 变色
        ColorAnimation { duration: 500 }
    }
}

通过这样定义的方式来写GUI, 是不是很清晰明了?

结论

我原先接触过WPF, 也是定义的方式来做的, 现在看到Qt也支持了, 感到蛮欣慰的. 我现在使用pyqt, 听说pyside的snapshot里面已经支持qml了, 期待稳定版本的发布.

看到网站上面有对应的截图, 个人猜测他们会把QML的内容整合到web上面. QML是支持javascript的, 具体内容可在 深入教程 里面, 可以看看.

为什么qt是最适合你的

| Comments

image

很多python开发人员, 在选择界面库的时候都会犹豫一段时间, 到底是选择tk, wxpython, pygtk, pyqt, 还是什么其他奇怪的解决方案. 这里, 我建议, 不要多想了, 节省你的时间和精力, 选择pyqt.

很多开发人员, 在选择界面库时候(至少是你能够选择的时候)都会犹豫很长时间, 到底是MFC, winform, WPF, flash, swing, VCL(还有人知道delphi吗), 还是什么其他奇怪的解决方案, 这里, 我建议, 不要多想了, 节省你的时间和精力, 选择pyqt.

好了, 广告时间结束, 这里说明理由:

  • 为什么python?

    • python是最好用最好学的编程语言(没有之一, VB不够好用)
    • 根据GUI的本质思维, 只有动态语言才能做到那么强的灵活性. 所以C/C++/java什么的不方便开发. 不然你动态生成页面试试?
    • 快速开发. 同样的功能, python的代码是其他语言的若干分之一. 代码量少了, 开发速度和质量都提高了.
  • 为什么Qt?

    • 工业级别的界面库. tk, wxpython都太简陋了, 不堪大用. wxpython还有性能问题. (by Jimmy Kuu)
    • 跨平台. VCL, winform, WPF, GTK可能好用一些, 但是跨平台试试? 以后一辈子和某个平台挂钩了. (好吧, GTK, mono可能多平台.. 但是没有pyqt好用)
    • 简单直观的事件处理方式. 只需要把一个signal插入到一个slot里面就好了, 并且是热拔插. callback机制已经过时了.
    • 性能很好, 足够工程使用. 一个实际的例子: linux下面的桌面系统KDE是基於Qt的.
  • 虽然pyqt很好, 但是还有其他需要注意的方面

    • 如果你是开源项目, 随便用. 但是如果是商业闭源, 那需要给 riverbank 他们交费, 一个开发者350英镑, 还是蛮划算的(比起开发时间来说). 或者你可以考虑 pyside?
    • 发布. 打包后的pyqt程序, 10M左右, 如果你对空间要求很严格的话, 就不适合了. 但是现在这个网络时代, 这应该不成问题. QQ都非常大了..
    • 性能. 如果你要画实时图片, 3d什么的, python恐怕太慢了, 老实用回C++吧.
    • 移动应用. meego 还早, 好像可以在android上面写python, 但是pyqt.. 哈哈哈.
    • web2.0. 好吧, jquery是你的武器. 写javascript去吧. 还有flash..

还有我一些其他的看法:

  • Qt的designer是杀手级的. 在linux下面. glader什么的都去死吧.
  • tk8.5好像解决了中文问题. 但是控件是不是太少了? 不够用阿.
  • wxpython太丑了. 哪里比得上Qt支持的qss方式动态设置风格.
  • pyGTK? 没用过. 有时间看看.
  • MFC sucks.
  • delphi. 编译飞快. 但是动态生成界面? 当年研究了很久..
  • WPF: WTF..
  • winform. 他们说不好, 我觉得还好吧?
  • android/iOS. 好吧, 我落伍了.

关于理性的设计

| Comments

image

上周六, 我参加了 pmcamp 的活动, 主要是为了看看传说中的 西乔. 见面后, 发现真人完全比不上twitter上面的照片…

不过她带给我们一个演讲<理性的设计>, 的确是非常的精彩, 整个过程我们都全神贯注地听, 获益也非常的多. 以至于在讲完之后的提问环节, 我们都以”西乔老师”作为尊称来提问.

具体内容可以见分享出来的 幻灯片 , 可能以后会有放出来的视频, 具体得看pmcamp的组织者了.

这里说下我个人的理解, 对于资深读者来说可以到此为止了. :)

和名字所说的一样, 整篇内容都是讲关于如何做设计的, 以及设计的整个思路, 其实是非常的理性的. 目标是理性的, 思路是理性的, 准则也是非常理性的… 设计并不像我们想象中的那么感性, 虽然会触发观者的感性思维, 但是设计的过程是可控的.

首先要说的是, 在解决问题之前, 最重要的是要问对问题. 做设计, 需要把握住几点: 受众.. 等一等, 这些都是幻灯片里面已经有的内容, 我不需要再说一遍了. 要补充的是: 无论是做什么事情, 都要先问对问题, 以及可以从受众, 目的, 语境这三个方面来考虑. 面对威权我们可以用言语来撼动, 面对拖拉机我们只有跑路—-看起来扯远了.

这里面提到一个重要的思想: 视觉习惯. 即利用用户的视觉习惯来带动他们, 按照我们希望的方法来”看”, 来”想”, 来”活动”, 这个对于任何的设计, 都是有用的. 不能让用户来学习我们的思维, 而是让用户不知不觉按照我们预设的道路走. 比如现在很多流氓网站上面的flash广告就很深得精髓.

然后下面就是具体的策略: 排列,强调,清晰,简洁,风格,语气, 按照我这个程序员的思想来说, 就是用用户的思维语言(主要还是人类视觉的识别方式)来表示信息, 去掉多余的信息, 强化需要表达的信息. 这里面我还获得了一个观念: 现代主义. 极简, 强烈对比, 内容间大段的空白, 符合网格. 用这样的方式来展示信息, 给人以强烈的信息刺激….. 恩, 看起来我的博客就是现代主义的. 有没有人注意到底下的版权声明?

我觉得, 里面讲的内容都是非常实战性的, 需要在实际工作中不断地运用, 成为我们的本能.. 对于任何人来说, 其实都会从事设计(至少设计自己的房间), 这里再次推荐 `幻灯片`_ 给大家.

恩, 还有, 她每天会推几个设计到 http://designlol.net/ 上面, 大家可以学习学习.