顯示具有 python 標籤的文章。 顯示所有文章
顯示具有 python 標籤的文章。 顯示所有文章

2010年1月12日 星期二

如何取得滑鼠座標

標題只是個晃子,
主要是要測試 http://tohtml.com/
效果好像真的不錯優~

#!/usr/bin/python
# -*- coding: utf-8 -*-

import win32gui
import time

while (True):
    x,y = win32gui.GetCursorPos()
    time.sleep(0.2)
    print x,y

2010年1月7日 星期四

Threading in Python

 
對現今的應用程式來說,
multi-thread 是不可或缺的部份。
那麼在 python 中要怎麼 implement multi-thread 呢?

這裡介紹兩個 python 所提供的 module -- thread and threading

thread module


使用 thread module 很像在 win32 的 CreateThread,
只是 CreateThread 換成了 thread.start_new_thread(function, args[, kwargs])
第一個參數就是 thread function,第二個參數是我們要傳進去的資料(tuples),
我們看一個簡單的例子:

#-*- coding: utf-8 -*-
#!/usr/bin/python

import thread
import time

def Threadfun(string, sleeptime, *args):
    while(True):
        print '{0}_{1}\n'.format(string, sleeptime)
        time.sleep(sleeptime)

if __name__ == "__main__":
    for i in range(1,5):
        thread.start_new_thread(Threadfun, ("ThreadFun", i))
    while(True):
        print 'MainThread {0}'.format(thread.get_ident())
        time.sleep(1)


以上就是很基本的建立多個執行緒的方法。

如果需要同步呢?
我們可以用 Lock Object。
Lock Object 可以藉由呼叫 allocate_lock() 得到。
先看看沒有同步會是什麼情況。

#-*- coding: utf-8 -*-
#!/usr/bin/python

import thread
import time

def Threadfun(string, sleeptime, lock, *args):
    while(True):
        print 'Enter_{0}\r\n'.format(string)
        time.sleep(sleeptime)
        print 'Leave_{0}\r\n'.format(string)

if __name__ == "__main__":
    lock = thread.allocate_lock()
    thread.start_new_thread(Threadfun, ("ThreadFun1", 2, lock))
    thread.start_new_thread(Threadfun, ("ThreadFun2", 2, lock))

    while (True):
        pass


執行的結果如下:













看出來了嗎?一個都還沒離開,另一個就進去了。

使用 Lock Object 來保護:
#-*- coding: utf-8 -*-
#!/usr/bin/python

import thread
import time

def Threadfun(string, sleeptime, lock, *args):
    while(True):
        lock.acquire()
        print 'Enter_{0}\r\n'.format(string)
        time.sleep(sleeptime)
        print 'Leave_{0}\r\n'.format(string)
        lock.release()

if __name__ == "__main__":
    lock = thread.allocate_lock()
    thread.start_new_thread(Threadfun, ("ThreadFun1", 2, lock))
    thread.start_new_thread(Threadfun, ("ThreadFun2", 2, lock))

    while (True):
        pass


執行結果如下:













這樣才是我們要的結果喔!
p.s. 根據 python 官方文件,thread module在 python 3.0 已經改為 __thread
可是還是建議使用高階的 threading module

threading module

threading module 裡面包含了以下 components
  • Lock object
  • RLock object
  • Semaphore Object
  • Condition Object
  • Event Object
  • Thread Object
threading.Thread

Thread Object wrapper 了 start_new_thread() 這個 function。

當我們呼叫 start() 時,就會自動去呼叫 run() ,
所以我們只能夠 override __init()__ 以及 run() 這兩個 method,
絕對不可以 override start()。

#-*- coding: utf-8 -*-
#!/usr/bin/python

from threading import Thread
import time

class MyThread(Thread):
    def __init__(self, string, sleeptime):
        Thread.__init__(self)
        self.sleeptime = sleeptime
        self.setName(str(sleeptime))
    def run(self):
        while(True):
            print 'Threadfun_{0}\r\n'.format(self.getName())
            time.sleep(self.sleeptime)
if __name__ == "__main__":
    thrList = [MyThread('ThreadFun', i) for i in range(1,5)]

    # thrList[0]~thrList[3]
    for i in range(0,4):
        thrList[i].start()
    # another way
    #for i in range(1,5):
    #    MyThread('ThreadFun', i).start()


另外一種作法是把 thread function 當參數,在建構物件時傳入,
那麼一樣在呼叫 start() 是,這個函式就會被執行。

threading.Condition

Condition 也是一種鎖,
也就是說它也提供 acquire/release 方法。
除此之外,
它還可以wait/notify/notifyAll,
但是要注意的一點是,
wait/notify/notifyAll 一定要跟 acquire/release 一起使用,
否則就會拋出 RuntimeError 異常。

我們可以用一個簡單的 productor/consumer 模型來當作範例。
#!/usr/bin/python
# -*- coding:utf-8 -*-

from threading import *
import time

class itemX:
    def __init__(self):
        self.cnt = 0

    def produce(self, num=1):
        self.cnt += 1

    def consume(self, num=1):
        if self.cnt:
            self.cnt -= 1
        else:
            print 'WARNING***********************WARNING'

    def isEmpty(self):
        return not self.cnt

    def getCount(self):
        return self.cnt

class Producer(Thread):
    def __init__(self, condition, item, sleeptime=2):
        Thread.__init__(self)
        self.con = condition
        self.item = item
        self.sleeptime = sleeptime

    def run(self):
        while (True):
            time.sleep(self.sleeptime)
            self.con.acquire()
            self.item.produce()
            print 'produce 1 product\r\n'
            print self.item.getCount()
            self.con.notifyAll()
            self.con.release()

class Consumer(Thread):
    def __init__(self, condition, item, sleeptime=2):
        Thread.__init__(self)
        self.con = condition
        self.item = item
        self.sleeptime = sleeptime
    def run(self):
        while (True):
            time.sleep(self.sleeptime)
            self.con.acquire()
            print '({0})enter'.format(self.getName())
            while self.item.isEmpty():
                print '({0})wait'.format(self.getName())
                self.con.wait()
            self.item.consume()
            print '({0})consume 1 product\r\n'.format(self.getName())
            print self.item.getCount()
            self.con.release()


if __name__ == "__main__":
    X = itemX()
    cond = Condition()
    Producer(cond, X).start()
    Consumer(cond, X).start()
    Consumer(cond, X).start()

    while (True):
        pass

threading.Event

event 是很常用的同步機制,
我們先用 event 改寫上一個例子,
再來討論它有什麼要注意的地方。

#!/usr/bin/python
# -*- coding:utf-8 -*-

from threading import *
import time

class itemX:
    def __init__(self):
        self.cnt = 0

    def produce(self, num=1):
        self.cnt += 1

    def consume(self, num=1):
        if self.cnt:
            self.cnt -= 1
        else:
            print 'WARNING***********************WARNING'

    def isEmpty(self):
        return not self.cnt

    def getCount(self):
        return self.cnt

class Producer(Thread):
    def __init__(self, condition, event, item, sleeptime=1):
        Thread.__init__(self)
        self.con = condition
        self.event = event
        self.item = item
        self.sleeptime = sleeptime

    def run(self):
        while (True):
            time.sleep(self.sleeptime)
            self.con.acquire()
            self.item.produce()
            print 'produce 1 product, remain({0})\r\n'.format(self.item.getCount())
            self.event.set()
            self.con.release()

class Consumer(Thread):
    def __init__(self, condition, event, item, sleeptime=1):
        Thread.__init__(self)
        self.con = condition
        self.event = event
        self.item = item
        self.sleeptime = sleeptime
    def run(self):
        while (True):
            time.sleep(self.sleeptime)
            self.con.acquire()
            print '({0})enter\r\n'.format(self.getName())
            #while self.item.isEmpty():
            while (True):
                print '({0})wait'.format(self.getName())
                self.event.wait()
                break
            self.item.consume()
            self.event.clear()
            print '({0})consume 1 product, remain({1})\r\n'.format(self.getName(), self.item.getCount())
            self.con.release()


if __name__ == "__main__":
    X = itemX()
    cond_Con = Condition()
    cond_Pro = Condition()
    event = Event()
    Producer(cond_Pro, event, X).start()
    Consumer(cond_Con, event, X).start()
    Consumer(cond_Con, event, X).start()

    while (True):
        pass


首先,
event 的 block 機制跟 condition 是很不一樣的,
對condition來說,notify 是一個通知,
有人在等,通知才有意義,
如果沒有人等,通知是沒有意義的。
notify 和 wait 是有序的,
就是說,假設通知先發生了,
這時候才有人開始等,
那麼是絕對等不到已經發生的那一個通知。

可是 event 不一樣,event 是一種狀態。
當 event 被設定了,
它就處於激發狀態,
只要狀態沒有改變,
任何時候都可以等得到。

為了強迫 consumer 一定要等到 event 才能 consume,
我們把上一個範例的 "while self.item.isEmpty():" 改成 "while (True):"
當然 Lock(Critical Section) 的保護也是必要的,
才不會發生多個 consumers 同時 consume 的情況(race condition)。

producer 的 Lock 在這個範例可以不要,
但如果有多個 producers 就一定要加,
特別要注意的一點是,
producer 用的 Lock 和 consumer 的必須要不一樣,
不然會出現 deadlock。

還有就是這邊的 event 是 manual reset,
也就是激發後必須手動呼叫 clear() 方法使其回到未激發狀態。

threading.Timer
Timer 也是 threading 的一個元件,
可以在指定的時間間隔後,執行某一個動作(函式),
例如:

#-*- coding: utf-8 -*-
#!/usr/bin/python

from threading import Time

def hello(msg):
    print msg

t = Timer(3, hello, ['Hello world'])
t.start()


Pooling Threads

最後想要討論一個有趣的東西。
不同的作業系統對可執行的 thread 限制都不一樣,
有時候我們擔心一次建立太多 thread 會造成系統(或程式)效能變差,
比如說我們只想建立兩個 threads,可是我們有十件工作要做,
那麼我們就可以用排程的概念來實做。

在程式的一開始先把兩個 thread 建好 (thread pool),
然後利用 python 的 Queue module,
把十件工作的資料都 put 進 Queue。
在 thread function 裡面會去 get Queue 的資料,
只要 get 到,thread 就會開始工作。

#!/usr/bin/python
#-*- coding:utf-8 -*-

from threading import *
import Queue
import time

class MyThread(Thread):
    def __init__(self, condition):
        Thread.__init__(self)
        self.cond = condition

    def run(self):
        print '{0} start\r\n'.format(self.getName())
        global cnt
        while (True):
            id = threadPool.get()
            if id != None:
            self.cond.acquire()
            print '{0}_{1}'.format(self.getName(), id)
            for x in xrange(101):
               cnt += x
               time.sleep(2)
            print 'cnt = {0}\r\n'.format(cnt)
            cnt = 0
            self.cond.release()
            threadPool.task_done()


threadPool = Queue.Queue(0)
condition = Condition()
cnt = 0
for i in xrange(2):
    MyThread(condition).start()

for i in xrange(10):
    threadPool.put(i)

threadPool.join()
print 'done'


在 thread function 最後呼叫 task_done() 是為了讓 Queue 知道這一個工作已經完成,
是給 Queue.join() 作為參考。

如果這些工作會存取到相同的資源,
還是記得要用 Lock 保護。

Queue module 是 thread safe,所以這樣的應用是沒有問題的。

2010年1月4日 星期一

wxPython (1)

1.Beginning

接下來試著用 wxPython 來開發 GUI 吧!
先產生一個最陽春的視窗,
#-*- coding: utf-8 -*-
#!/usr/bin/python

import wx

if __name__ == "__main__":
    app = wx.App()
    frame = wx.Frame(None, -1, 'wxPython Test')
    frame.Show()
    app.MainLoop()
沒看錯,真的十行有找。
執行的結果,













這個視窗還有很多參數可以設定,
wx.Frame有一些method可以調整視窗的大小和位置。
  • Move(wx.Point point)
  • MoveXY(int x, int y)
  • SetPosition(wx.Point point)
  • SetDimensions(wx.Point point, wx.Size size)
  • Centre()
  • Maximize()
  • Minimize()
以上是使用 wx.Frame 這個基本框架類別產生出來的視窗,
那如果要在裡面加上其他控制項呢?
我們就要繼承這個類別,
所有的初始化就可以寫在__inti__()裡面。
#-*- coding: utf-8 -*-
#!/usr/bin/python

import wx

class myFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, (400,300))
        self.Show()
if __name__ == "__main__":
    app = wx.App()
    frame = myFrame(None, -1, 'wxPython Test')
    app.MainLoop()
執行的結果跟上面是一樣的。

2.Menu Bar

加 menu bar 的作法。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx

class myFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, (400,300))
        
        menubar = wx.MenuBar()
        
        file = wx.Menu()
        file.Append(1, 'New', 'New a file')
        file.Append(1, 'Quit', 'Quit application')
        
        edit = wx.Menu()
        edit.Append(3, 'Copy', 'Copy')
        edit.Append(4, 'Paste', 'Paste')
        
        menubar.Append(file, '&File')
        menubar.Append(edit, '&Edit')
        self.SetMenuBar(menubar)
        
        self.Centre()
        self.Show()
        
if __name__ == "__main__":
    app = wx.App()
    frame = myFrame(None, -1, 'wxPython Test')
    app.MainLoop()
首先要先建立一個 MenuBar 物件,
menubar = wx.MenuBar()
接著建立 Menu
file = wx.Menu()
然後把 Menu 放到 MenuBar 裡面。
執行出來就像這樣。














在 append menu 的時候,&符號是用來建快速鍵,
當我們按 Alt 鍵,menu 的第一個字母就會出現底線,
此時我們可以接著按那個字母當作快速鍵,例如 Alt + F 或是 Alt + E。














當點了某一個 menu,會產生一個 event,
當然我們必須手動把這兩個東西 bind 在一起。
#!/usr/bin/python
# -*- coding: utf-8 -*-
import wx

class myFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, (400,300))
        
        menubar = wx.MenuBar(wx.MB_DOCKABLE)
        
        file = wx.Menu()
        file.Append(1, 'New', 'New a file')
        file.Append(1, 'Quit', 'Quit application')
        
        edit = wx.Menu()
        edit.Append(3, 'Copy', 'Copy')
        edit.Append(4, 'Paste', 'Paste')
        
        menubar.Append(file, '&File')
        menubar.Append(edit, '&Edit')
        self.SetMenuBar(menubar)
        
        # Bind menu with an event
        self.Bind(wx.EVT_MENU, self.OnQuit, id=2)
        
        self.Centre()
        self.Show()
        
    def OnQuit(self, event):
        self.Close()
        
if __name__ == "__main__":
    app = wx.App()
    frame = myFrame(None, -1, 'wxPython Test')
    app.MainLoop()
注意 bind 的地方跟 OnQuit 這個 method 就是響應 menu 這個 event。

以上都是固定的 menu ,那如果要產生 popup menu 呢?
#-*- coding: utf-8 -*-
#!/usr/bin/python

import wx

class MyPopupMenu(wx.Menu):
    def __init__(self, parent):
        wx.Menu.__init__(self)

        self.parent = parent

        minimize = wx.MenuItem(self, wx.NewId(), 'Minimize')
        self.AppendItem(minimize)
        self.Bind(wx.EVT_MENU, self.OnMinimize, id=minimize.GetId())
        
        close = wx.MenuItem(self, wx.NewId(), 'Close')
        self.AppendItem(close)
        self.Bind(wx.EVT_MENU, self.OnClose, id=close.GetId())

    def OnMinimize(self, event):
        self.parent.Iconize()

    def OnClose(self, event):
        self.parent.Close()
        

class myFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, (400,300))

        menubar = wx.MenuBar(wx.MB_DOCKABLE)
        
        file = wx.Menu()
        file.Append(1, 'New', 'New a file')
        file.Append(2, 'Quit', 'Quit application')

        edit = wx.Menu()
        edit.Append(3, 'Copy', 'Copy')
        edit.Append(4, 'Paste', 'Paste')
        
        menubar.Append(file, '&File')
        menubar.Append(edit, '&Edit')
        menubar.Append(MyPopupMenu(self), 'pop')
        self.SetMenuBar(menubar)

        #Bind menu with an event
        self.Bind(wx.EVT_MENU, self.OnQuit, id=2)

        #Bind right-button down event
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightBtnDown)

        self.Centre()
        self.Show()

    def OnRightBtnDown(self, event):
        self.PopupMenu(MyPopupMenu(self), event.GetPosition())
        
    def OnQuit(self, event):
        self.Close()

if __name__ == "__main__":
    app = wx.App()
    frame = myFrame(None, -1, 'wxPython Test')
    app.MainLoop()
有注意到嗎?
在 OnRightBtnDown 這個 method 裡面,
我們呼叫 PopupMenu ,傳進去的第一個參數是一個繼承 wx.Menu 的物件,
當然也可以是一個 wx.Menu 物件喔!
也就是說,我們也可以這樣寫:
def OnRightBtnDown(self, event):
    #self.PopupMenu(MyPopupMenu(self), event.GetPosition())
    self.Bind(wx.EVT_MENU, self.OnTest, id=5)
    test = wx.Menu()
    test.Append(5, 'test', 'test')
    self.PopupMenu(test, event.GetPosition())

def OnTest(self, event):
    self.Close()

2009年12月31日 星期四

byte of python 小筆記

 
雖然已經寫過一些小程式,
最近還是強迫自己把"byte of python"看了一次,
看看是不是有什麼遺漏的部份。

1. 不定參數

python 也有不定參數的用法,
其中分為 list 和 dictionary

#!/usr/bin/python
# Filename: total.py
def total(initial=5, *numbers, **keywords):
    count = initial
    for number in numbers:
        count += number
    for key in keywords:
        count += keywords[key]
    return count

print(total(10, 1, 2, 3, vegetables=50, fruits=100))


其中,
10, 1, 2, 3會被當成 list 傳進函式中,

vegetable=50, fruit=100會被當成 dictionary 傳進函式中。

2. Tuple

Tuple 跟 list 很類似,但是功能沒有那麼多,最重要的差異是,tuple 是不可變的 (immutable),
定義 tuple 的方式是用小括號對 (parentheses)

zoo = ('python', 'elephant', 'penguin')

那麼如何定義空的或是只有一個元素的 tuple 呢?

# 空的tuple:
myempty = ()

# 只有一個元素的tuple:
singleton = (2, )

3. Set

python內建的資料結構除了 list, tuple,dictionary 外,
還有一種就是 set。

set裡面沒有順序,每個元素也沒有數量,
而最好用的莫過於集合的交集,聯集,差集之類的運算。

4. The format Method

有時候我們需要把字串跟一些資訊結合在一起,format 是很好的方法。

age = 25
name = 'Swaroop'
print('{0} is {1} years old'.format(name, age))
print('Why is {0} playing with that python?'.format(name))

進階用法 :
>>> '{0:.3}'.format(1/3) # decimal (.) precision of 3 for float
'0.333'
>>> '{0:_^11}'.format('hello') # fill with underscores (_) with the text
centered (^) to 11 width
'___hello___'
>>> '{name} wrote {book}'.format(name='Swaroop', book='A Byte of Python')
# keyword-based
'Swaroop wrote A Byte of Python'



5. Object Oriented (class)

self 就等同於C/C++中的 this 指標。

==
而__init__就等同C/C++中的建構子,
    __del__就等同C/C++中的解構子。

這兩個 method 都是自動被呼叫的,
其中__init__當然是物件建立時會被呼叫,
__del__是當物件不再被使用時會被呼叫,
但是不保證何時__del__會被呼叫,
所以我們也可以強制使用 del 來呼叫它。

==
python 的類別也有 static method,
有兩種用法
一種是像一般 method 一樣宣告,
但呼叫時加上 staticmethod(),例如

def howMany():
    '''Prints the current population.'''
    print('We have {0:d} robots.'.format(Robot.population))
howMany = staticmethod(howMany)

另一種是宣告時就加上裝飾字 (@staticmethod)

@staticmethod
def howMany():
'''Prints the current population.'''
print('We have {0:d} robots.'.format(Robot.population))


==
在 python 中,
所有的 class members 都是 public,而所有的 class methods 都是 virtual,
那如果需要 private 的 class member 呢?
只要在變數前加兩個底線就可以了,
像是 __privatevar 。

==
python 是支援多重繼承的,
在繼承的情況下,
如果有找不到的 method,
它也會往 base class 去找喔。

6. Pickle Module

有一個沒用過的東東,pickle,
pickle可以把任何物件存成檔案,
當然也可以從檔案載入,
哇屋,這真是超有用的 :)

#!/usr/bin/python
# Filename: pickling.py

import pickle

# the name of the file where we will store the object
shoplistfile = 'shoplist.data'
# the list of things to buy
shoplist = ['apple', 'mango', 'carrot']

# Write to the file
f = open(shoplistfile, 'wb')
pickle.dump(shoplist, f) # dump the object to a file
f.close()

del shoplist # destroy the shoplist variable

# Read back from the storage
f = open(shoplistfile, 'rb')
storedlist = pickle.load(f) # load the object from the file
print(storedlist)


7. Logging Module

當我們想把一些除錯訊息輸出到檔案時,
這個模組就非常好用了。
只要給檔案路徑跟基本設定就可以了。

#!/usr/bin/python
# Filename: use_logging.py
import os, platform, logging
if platform.platform().startswith('Windows'):
    logging_file = os.path.join(os.getenv('HOMEDRIVE'),
    os.getenv('HOMEPATH'), 'test.log')
else:
    logging_file = os.path.join(os.getenv('HOME'), 'test.log')

logging.basicConfig(level=logging.DEBUG,
     format='%(asctime)s : %(levelname)s : %(message)s',
     filename = logging_file,
     filemode = 'w',
)
logging.debug("Start of the program")
logging.info("Doing something")
logging.warning("Dying now")


8. List Comprehension

這真的是很"技巧性"的寫法。

listone = [2, 3, 4]
listtwo = [2*i for i in listone if i > 2]
print(listtwo)


9. 其他

exec, eval, repr (這三個不寫,考驗我的記憶)