网络寻租

Programmer, Gamer, Hacker

PyQt4的异步消息机制

| Comments

因为工作需要,要在GUI的后台维护一个监控线程,该线程需要和主线程做通讯。为了完成这个需求,需要采用一种线程安全的消息机制。因为我采用PyQt4作为GUI的库,因此,直接使用PyQt4的消息机制成为我考虑的首选。

在PyQt4里面,发出消息采用emit的方式。比如:

1
2
3
4
# 新的pythonic的方式
self.sbValue.setValue[int].emit(12)
# 旧的
self.sbValue.emit(SIGNAL("setValue(int)"), 12)

但是放到多线程里面,会发生什么事情呢?

先解释一下消息处理的原理。我们知道,GUI之所以能够处理消息,是因为GUI主线程在初始化完毕的时候,会运行一个ecec_()方法,在这个方法里面,线程不断地检查消息队列,看是否有新的消息发出来,如果有,就处理它。这就是我们说的:event loop。

然后看文档。 Qt在多线程下的消息传递机制 :

image 里面一段话:

Qt supports three types of signal-slot connections: * With direct connections, the slot gets called immediately when the signal is emitted. The slot is executed in the thread that emitted the signal (which is not necessarily the thread where the receiver object lives). * With queued connections, the slot is invoked when control returns to the event loop of the thread to which the object belongs. The slot is executed in the thread where the receiver object lives. * With auto connections (the default), the behavior is the same as with direct connections if the signal is emitted in the thread where the receiver lives; otherwise, the behavior is that of a queued connection.

也就是说,有3种机制: * 采用直接调用的方式。类似于callback。 调用者线程直接去执行被调用的方法。 * 队列方式。调用者线程把消息丢给消息队列,然后就不管它了。被调用者的线程在处理消息队列的时候,就会处理它。 * 自动方式(默认)。如果调用者和被调用者是属于一个线程的,系统选择直接调用,不然就采用队列的方式。

而机制的选择,可以在connect方法里面设置type:

1
2
3
4
5
bool QObject::connect (const QObject * sender,
                       const char * signal,
                       const QObject * receiver,
                       const char * method,
                       Qt::ConnectionType type = Qt::AutoConnection)

那麽,我们可以开始做实验验证这样是否在PyQt4里面行得通了。

测试代码1:测试在单线程下,修改type之后,emit消息是否会立刻执行下一行代码:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
线程中发消息
"""
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import threading
import time

class Main(QDialog):
    def __init__(self):
        QDialog.__init__(self)
        self.pbProcess = QPushButton("&OK")
        self.leResult = QLineEdit("")

        layout = QHBoxLayout(self)
        for w in self.pbProcess, self.leResult:
            layout.addWidget(w)

        self.connect(self.pbProcess, SIGNAL("clicked()"),
                     self.process)
        self.connect(self, SIGNAL("waitProcess()"),
                     self.waitProcess)#, Qt.QueuedConnection)

    def process(self):
        threading.Thread(target=self.threadRun).start()

    def threadRun(self):
        time.sleep(3)
        self.emit(SIGNAL("waitProcess()"))
        print "sended.."

    def waitProcess(self):
        time.sleep(3)
        self.leResult.setText("hello!")

def main():
    app = QApplication([])
    Main().exec_()

if __name__=="__main__":
    main()

点击按钮,”sended!“要过一段时间才出现在命令行。 把上面的注释部分改为:

1
2
self.connect(self, SIGNAL("waitProcess()"),
             self.waitProcess, Qt.QueuedConnection)

再执行一次,点击按钮,”sended!“立刻就出现了。

测试代码2:现在转到线程里面:

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python
#-*- coding:utf-8 -*-
"""
线程中发消息
"""
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import threading
import time

class Main(QDialog):
    def __init__(self):
        QDialog.__init__(self)

        self.pbProcess = QPushButton("&OK")
        self.leResult = QLineEdit("")

        layout = QHBoxLayout(self)
        for w in self.pbProcess, self.leResult:
        layout.addWidget(w)

        self.connect(self.pbProcess, SIGNAL("clicked()"),
        self.process)
        self.connect(self, SIGNAL("waitProcess()"),
        self.waitProcess)#, Qt.QueuedConnection)

    def process(self):
        threading.Thread(target=self.threadRun).start()

    def threadRun(self):
        time.sleep(3)
        self.emit(SIGNAL("waitProcess()"))
        print "sended.."

    def waitProcess(self):
        time.sleep(3)
        self.leResult.setText("hello!")

def main():
    app = QApplication([])
    Main().exec_()
    #app.exec_()

if __name__=="__main__":
    main()

执行后,前3秒线程等待,GUI不会卡死。然后线程打印:”sended..“,然后GUI卡死3秒,然后GUI显示”hello!”。

PyQt4的皮肤效果

| Comments

PyQt4提供了非常好的皮肤机制,可以作出非常炫的效果。其中一个功能就是qss,利用类似css的方式来配置界面。 文档在这里。 使用方法(顺便分享一下我的qss配置):

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*背景贴图*/
QWidget{ background-image: url(res/tex.png) }
QAbstractScrollArea,QPushButton { background-image: None}

/*背景色*/
QWidget{ background-color: bgdColor }
QPushButton,QSplashScreen{background-color: None}

/*menu*/
QMenuBar {
background-color: qlineargradient(x1:0, y1:0, x2:0, y2:1,
stop:0 lightgray, stop:1 darkgray);
}
QMenu {
margin: 2px; /* some spacing around the menu */
}
QMenu::item:selected {
border-color: borderColor;
background: rgba(100, 100, 100, 150);
}

/*按钮*/
QPushButton{
border: 3px outset borderColor;
background-color: bgdColor;
padding: 5px;
border-radius: 5px;
}
QPushButton:checked {border-color: red}
/*醒目按下的按钮*/
QPushButton:pressed {border-color: green}

/*QSlider*/
QSlider::groove { background: gray; }
QSlider::groove:horizontal { height: 6px;}
QSlider::groove:vertical { width: 6px;}
QSlider::handle {
border: 1px solid borderColor;
border-radius: 3px;
background: bgdColor;
}
QSlider::handle:horizontal { width: 18px; margin: -3px 0 ; }
QSlider::handle:vertical { height: 18px; margin: 0 -3px ; }
QSlider::add-page { background: gray; }
QSlider::sub-page { background: green;}

/*QTableView*/
QTableView {
alternate-background-color: darkContentColor;
background-color: contentColor;
}

上面作为字符串放在DEFAULT_STYLE里面, 然后具体的颜色在下面的python代码中设置:

1
2
3
4
5
6
7
8
9
10
11
12
#设置style里面的颜色
COLOR_MAP = {
'borderColor':'gray',
'bgdColor':'#FCF6E4',
'contentColor':'#E1EDFB',
'darkContentColor':'#CDE2F8',
}
for k,v in COLOR_MAP.iteritems():
DEFAULT_STYLE = DEFAULT_STYLE.replace(k, v)

app = QApplication([])
app.setStyleSheet(DEFAULT_STYLE)

显示的效果如下:

image

安利模式做教育

| Comments

image

安利模式的问题

考虑安利的商业模式,每次交易发生在上线与下线之间,交易的成本是上线在下线身上投入的时间.

如果只是卖商品,直接营销的成本往往很大,而这部分成本是会转嫁给下线的,整个过程不会创造价值,或者整个体系的价值产出为负.

具体分析在这里: http://www.impencil.org/?p=7486

替代方案

我考虑了一下,如果采用教练指导作为商品,这样的模式可以带来价值. 因为: * 教练指导是对下线有价值的 * 而这个价值不容易通过其他渠道来体现

什么是教练指导:

  • 在目标和方法都能够确定的前提下,教练指导学员一步步完成整个学习或训练过程.
  • 教练模式需要教练根据每个学员的状况作指导,必须是一对一的关系.
  • 普通人很难获得一个称职靠谱的教练.

作为商业模式来讲,下线可以通过上线的教育获得直接的收入提高,这个模式是可以带来价值的.

计划

这个模式的重点在于上线和下线的关系.为了保证这个模式成功,需要有以下几点:

  • 上线和下线能够保持一个稳定的关系
  • 下线还能继续发展下线
  • 防止体系”腐化”,像安利一样成为一个无法提供价值的体系

因此,考虑以下几个注意的方面:

  • 下线必须是”空白”人士,方便灌输
  • 上线拥有足够的指导能力,能够指导下线提升收入
  • 下线提升收入后,还是可以通过上线的指导继续提升
  • 上线鼓励下线发展下下线,提升双方的共同受益
  • 不会有滚雪球效应,防止其中一方独立门户

具体实施方法(初稿):

上线主动联系下线,说服下线,以工资收入的百分比方式获得教练费用.为了提升说服力,可以设定期望,没有满足期望的时候,上线返回若干钱.以合约的方式确立起来.

上线采用一系列方式教育下线,这个过程可以不是全职:上线最好有工作,才能保证上线自己足够的成长程度.

当上线无法提供价值给下线的时候,合约终止,因此上线必须时刻提升自己的价值产出.合约本身是制式文件,限制上线对下线采用洗脑的方式榨取收入,同时由于是双方签订合约,出现问题也与上线的上线无关.

上线可以通过培训下线的能力,让下线继续发展下下线,提升双方受益.

传输这样的观念:每个人必须要有教练,时刻监控自己的成长.

提供平台,让所有的成员都在这个平台上面交流,发展,防止社区分裂.

问题

以上只是初步的分析,但是也暴露出来几个问题点:

  • 如何吸引新人加入,收入分成的方法是不为绝大多数人接受的
  • 解法:需要这个体系能真正提升价值,然后新人才会希望加入
  • 如何保证关系持久,下线可能因为不值的原因取消合约,上线也可能因为不值的原因取消合约.
  • 解法:无法保证.但是可以让双方养成发展下线的习惯,新的合约建立速度大于旧的合约解除的速度即可.
  • 社区分裂:无法保证该模式不被其他人学去,并建立其他”邪恶”的组织.
  • 解法:需要能够提供一个稳定的,不会被夺取的上游价值提供源,比如通过该网络发送讯息,提升网络中人士的职业发展机会.这个平台直接通过上线下线获取收入和讯息,维持平台的运转.

结论

以上都是泛泛而论,需要通过进一步的实践来验证理论的有效和可行性.

可以这样做吗

| Comments

image

在超市里, 肚子饿了, 直接拿袋食物开封就吃, 但是不要忘了出去的时候付账.

一本书感觉不错/糟糕, 直接通知作者本人, 告诉他哪里不错/糟糕.

走在路上, 发现一个人吸引了自己的注意力, 走上前去告诉他/她, 你吸引了我,以及为什么吸引我.

不在意亲戚的想法, 做自己想要做的事情, 等做成了, 再通知他们.

路上遇到一个陌生人, 假装认识他, 和他寒暄.

如果已经离开了校园, 找一所大学正在上课的教室进去听课, 提问, 和其他同学交流.

到一个公司/单位/政府部门, 观察其中的一个人, 去和他交朋友.

每个月把家里重新装饰一遍

没钱买房, 去租一个别墅住一段时间.

工作无聊了就辞职.

让自己领域里面的人知道自己的存在, 如果不是通过实力, 至少通过特立独行.

把自己的一段时间卖给陌生人.

如果想做什么事情, 立刻就去做, 即使是离开自己当前住的地方, 即使是离开自己当前的工作, 即使自己当前的工作收入不菲

尝试同性爱, 异性爱, 以及嫖妓.

C语言如何实现RAII

| Comments

资源的获取与释放,C语言里面是要让程序员考虑的,比如

1
2
3
4
5
6
void work(){
  Buffer b;
  init(b);
  do_sth(b);
  del(&b);
};

在C++里面,有raii这样很方便的特性,当离开作用域的时候,自动释放资源,如

1
2
3
4
void work(){
  Buffer b;
  do_sth(b);
};

我在想,如何让C支持这样的特性?于是就有了下面这个宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define using(b) \
for(int i=0; i<2; i++){\
if (i==0) \
  { init(b); } \
else \
  { if (i==1) \
    { del(b); \
      break; \
    }; \
  }; 
void work() {
  Buffer b;
  using(&b)
    printf("working..\n");
  };
};

edit: 被批了,C就按照C的方式干活,RAII交给C++.

DwarfFortress介绍

| Comments

什么是dwarf fortress?

一个建设矮人部落的游戏. 随机生成世界地图, 历史, 超级复杂的建设…

http://www.bay12games.com/dwarves/screens.html

有人甚至用部落和矮人实现出了一个 计算机 (有种3体里面那个人计算机的感觉)

免费的. 有linux版本可以下.

一个漂亮的 显示工具:

image 这个工具可以在linux下面用cmake编译,但是我没有编译成功, 摸索中.

有一些其他的类似的游戏, 整理在这里:

http://www.townsgame.com/

http://www.goblincamp.com/

http://www.paradoxplaza.com/games/a-game-of-dwarves

http://gnomoria.com/?page_id=15

为什么用ReST写博客

| Comments

什么是ReST

ReST全称是reStructuredText,是一种容易写,容易看的文本格式,

具体介绍在这里: http://docutils.sourceforge.net/rst.html

如果你看不懂英文,可以看———好吧,我没有找到中文的介绍.

ReST的好处

好处就是:以后再也不用为了调整格式而费心啦,专心写文章.

当然了,遇到复杂的状况(比如写书,写公式,图表之类),还需要其他的工具.

对于写博客和简单的文档来说,是非常适合的.

如何发布到博客上

写好ReST后,把文本拷贝到: http://www.tele3.cz/jbar/rest/rest.html

这是一个在线生成工具,生成html之后,复制到博客里面发布就可以了.

记得保存备份,说不定以后什么时候还要用到的说.

结论

  • 中文资料比英文资料少多了,所以好好学英文吧.
  • 简单就是美.

三一学会

| Comments

需求

我发现在学校之外, 以及学校里面, 都有许多的人, 希望上进, 但苦于:

  • 一个人很难分析出自己真正想要什么, 也无法监督自己执行.
  • 自己学习无法保证按照计划执行.
  • 由於自我的局限性, 无法看得更高, 更远.

因此, 我希望建立一种机制, 能够帮助这些人解决关于学习, 理想, 以及持续进步的问题.

思路

我发现, 有几个方法可以用来解决上面的问题:

  • 建立学习的环境.
  • 教练指导.
  • 教学相长.

实现

现在我还只是在尝试过程中, 采用以下解决方案:

  • 住在附近, 有志提高自己的人, 休息时间聚集在一起, 共同学习.
  • 大家互相监督, 保证持续的时间投入.
  • 学会一个概念后, 向其他人教授该概念, 达到共同学习, 以及加强理解的目的.
  • 成员互相的关系应该是教练<–>学生的关系. 有教练经验的成员指导他人明确目标, 制定计划, 执行计划.
  • 成员必须把发展下线, 即自己做教练, 作为成长后的目标之一, 吸引其他有上进心的人加入学会.
  • 为了效率, 一起学习的人数必须有限, 太多了, 就要分拆成两个小组.

现在我会在生活中实验这种模式, 以及做推广与研究, 这种学习小组就叫三一学会吧. 随便起的.

当解决方案成熟之后, 我希望能够把这种方案, 以类似病毒发展方式推广到全中国.

文本方式写幻灯片

| Comments

为什么要用S5?

  • 纯文本
  • html

S5实例

1
2
3
4
5
6
7
8
9
10
<!-- 这是一张slide -->
<div class="slide" id="id1">
    <!-- 这是slide的标题 -->
    <h1>为什么要用S5?</h1>
    <!-- 这是slide的内容 -->
    <ul>
        <li>纯文本</li>
        <li>html</li>
    </ul>
</div>

rst2s5实例

代码

为什么要用S5?
================================

* 纯文本
* html

试用一下

不多说, 在线rst2s5 : http://server.linjunhalida.com/tools/rst2s5/

本地生成

need docutils:

rst2s5 xxx.rst xxx.html
rst2s5 --theme small-white xxx.rst xxx.html
rst2s5 --theme-url http://xxx.com/ui/small-white xxx.rst xxx.html

如何写rst2s5?

写代码

代码:

1
2
3
4
5
6
7
8
9
10
from docutils.core import publish_string

overrides = {'theme': '',
             'theme_url': '/statics/S5/ui/small-white',}

def rst2S5(string):
    return publish_string(
        source=string,
        settings_overrides=overrides,
        writer_name='s5')

资源

手机分配短讯id的面试题目

| Comments

题目

TopLanguage论坛里有讨论一个面试题目,内容如下:

有个老的手机短信程序,由于当时的手机CPU,内存都很烂。所以这个短信程序只能记住256条短信,多了就删了。

每个短信有个唯一的ID,在0到255之间。当然用户可能自己删短信,现在我们收到了一个新短信,请分配给它一个唯一的ID。说白了,就是请你写一个函数:

byte function(byte* ids);

该函数接受一个ids数组,返回一个可用的ID,由于手机很破,我要求你的程序尽量快,并少用内存。注意:ids是无序的。

Miro的分析在这里: http://www.cnblogs.com/miloyip/archive/2010/08/31/idalloc_clarify.html

我的分析

我觉得,因为短消息发送的频率很低,那么我们不需要考虑取ID和释放ID的响应速度问题,主要问题放在如何节约空间.那么,最节约空间的是按照比特来存储ID是否使用.

还有就是,重新整理了需求,需要提供一个释放和获取ID的接口.

解法

代码如下,没有测过,保证有错:

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
29
#define SIZE_COUNT 256/8

struct manager {
    byte map[SIZE_COUNT];

    byte alloc();
    void dealloc(byte id);
};

//获取ID    
byte alloc(){
    for(int i=0; i<SIZE_COUNT; i++){
        byte data = map[i];
        if (data == 255) continue; //全满了

        for(int j=0; j<8; j++){
            if (((data >> j) & 1) == 0) {
                //got it!
                data |= 1<<j;
                return i*8 + j;
            }
        }
    }
}

//释放ID    
dealloc(byte id){
    map[id/8] &= ~(1<<(id % 8));
}

结论

上面的解法速度上还是很慢的,如果ID空间长期处于半饱和,那么每次获取都要循环一遍数组,那么效率无法接受,因此要考虑更复杂的链表方式,但是这样一来空间就不会最小了.