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

2014年7月13日 星期日

how to resize the console window in windows

最近在軍中時在遇到太多鳥事,同時也遇到一些不太如意的事情,再加上我開始有點迷茫了,關於學習方式的事 :( 所以已經有一段時間沒碰程式了,只有回家逛逛論壇,突然我看到一個議題讓我有點小興趣,那就是我今天要寫的東西。

其實如果想要可以放大的console window,其實也不難而且也不需要寫程式,點個右鍵,去設定裡面的一些性質就可以,只是身為一個programmer,就是要寫程式去控制不是嗎? :)


上面這張圖是我目前寫出來的樣子,只是我對某些地方仍然感到不解,就是當我點把視窗放到最大,他其實並非真的完全放大,下面仍然會有些空隙,目前的code我放在github上面的console.py,其實這個project當初只是無聊試試看socket做個小小的consle介面的聊天室而已。

關於撰寫這樣的功能,需要注意的地方,大概也只有幾點,這些心得是我觀察了一下,console的內容後得出來。

首先,先來說說我的觀察,一開始windows的console window buffer的預設值是 80x300,我是不知道是不是每個windows系統都這樣啦,或許你自己也改過設定也說不定,之後你可以試試看點放到最大,你會發現視窗只有拉長高度,對於寬度他並沒有拉寬,所以可想而知,console buffer size,是一個關鍵點!! 所以想要放大視窗,我們必須為console準備相對應的buffer size,至於要怎麼計算呢? 我認為大概是這樣的

console buffer width = screen width / console font width
console buffer height = screen height / console font height

以我電腦為例
200 = 1600 / 8
56 (大概值) = 900 / 16
也就是說最少我也需要200x56的buffer size才可以放到最大....

console font size的預設值通常是 8x16,當然關於這個可用winapi去取得,是用什麼函式這裡就不囉嗦了,詳細請看console.py裡的resizeConsoleWindow,算完後,要重新設定buffer size以至於放大時可以填充,我同時也設定了console window info 雖然有得到跟上面那張圖,不一樣的結果(更接近真的放大最大),上面那張是按了放大最大後的結果,關於這個行為我目前還是不解就是了,因為我已經準備足夠的buffer size讓他可以最大化,可是點了之後卻有點落差,不是很懂為什麼? 關於這點我還必須在研究一下 :(

或者有誰知道為何的,可否告知小弟 :)

2014年5月10日 星期六

read open source pygments 2

這篇就來,繼續上次的進度 :)

上次code貼上來的結果不甚理想,所以我在想是不是要自己來寫一個format會比較方便,當然在動手之前,我想重新理解code的流程是必須的,因為要讓自己對這份open source掌握更加清楚,所以我決定利用debug的step into,來進行trace code :p

這邊就來介紹一下我用的工具,關於debug,我還是選擇用IDE,因為比較直覺化又容易使用,我是覺得沒必要去聽信那些說用gdb才是王道,畢竟那要打一些指令,實在是有點麻煩,個人是認為有興趣的話再去用那種方式 :p

說要介紹我是用哪款IDE都差點忘了 :) 這邊我是用pycharm ,這一直以來都算很有名氣,雖然是要錢的,但是網路上總有...咳咳,不過那僅限以前!!,現在可好了呢,它有提供open source版,而且功能很夠用,有興趣的人可以去載來用看看 :)



pycharm的介面如上,它提供了算是不錯的code intellisense,加上trace code也是方便,像是find definition,加上它的plugin也是挺多的,要說讓我說嘴的地方就是記憶體吃的有點多:(
另外以介面來講,我還是喜歡sublime就是了,好啦,該來講講怎麼trace code的部分了,一般來講都要先下個所謂的break point,就是讓程式執行到該行時,停下來,接著就有如下幾個操作
1. next line(step over)
2. step into
3. step out
... 等等

就先來看看這張圖吧

我在40行的地方下了中斷點,所以程式執行到這邊就會停下來,之後呢,我特別在watch中加入一些我想看的變數,上面這張圖是我step over後,可以看出它是一個class,有興趣要看它是怎麼一步一步產生的話,就必須用step into,就像接下來的HtmlFormatter,我用了step into就會如下面這張圖


程式碼頁面會跳到HtmlFormatter所在的檔案html.py,接著你更想的話,你當然可以繼續用step into,就看你想要鑽到的地方有多深,就跳多深吧 :)

另外我覺得用pycharm來debug真的是頗爽 :) 老實講真的太方便了,看前面幾張圖就知道,它其實會自己監看一些變數,起初我用sublime text閱讀源碼時,其實有點卡卡的,當時我認為是我理解力不夠,因為有很多地方看過去,我會有種不知道這樣的code會產生什麼效果,但是用了pycharm就方便多了,因為你會看到這些變數,就知道他們是否如你所想像的一樣,這樣一來多了一些證實,自然就會多了信心,閱讀起來更加順遂,就舉個例子好了,請看下面這張



我當初一眼看沒有懂的地方就是這裡
arg = ('cssclass' in self.options and '.'+self.cssclass or '')
從圖中可以看到arg的值是.highlight其實就是'.'+self.cssfile
很好!! 這樣的寫法到底是怎麼運作呢? 詳細可以看看 這個這樣的寫法不是每個programming language都可以寫的呢 :) 看看這個wiki 就知道囉~

回到正題,那行的意思是 'cssclass'有沒有在self.options裡,有的話就會回傳'.'+selfclass,沒有的話,就會繼續evaluate下去,那就是變成回傳'',這其實是用到short circuit的技術 :p
其實個人認為這邊根本不需要用到這種寫法,只是徒增不可讀性就是了,用if else還親民多了,不過這不代表這樣的寫法沒用處,我想大概可以用在 lambda這地方吧 :)

經過這次的閱讀,目前我想到一個,不用自己繼承一個HtmlFormatter的寫法了,我是用了擷取部分code,之後再自行增加一些必須的html的方式。

目前我粗略地寫了個簡單測試腳本,效果如下

 1 '''
 2 a script for python code highlighting is expected to partial html output.
 3 That's output content without the whole html tags.
 4 
 5 
 6 ex.
 7 <style>
 8 xxx
 9 xxx
10 xx
11 </style>
12 <table>
13 <div>
14 xx
15 xx
16 </div>
17 </table>
18 
19 usage: test.py inputfile outputfile
20 
21 '''
22 
23 
24 from pygments.formatters import HtmlFormatter
25 from pygments import highlight
26 from pygments.lexers import *
27 from pygments.styles import *
28 import sys
29 
30 try:
31     readfile = open(sys.argv[1], 'r')
32 except:
33     sys.exit()
34 
35 code = readfile.read()
36 readfile.close()
37 
38 file = open(sys.argv[1].split('.')[0]+'.html', 'w')
39 
40 st = get_style_by_name('native')
41 myformat = HtmlFormatter(style=st, linenos='inline')
42 
43 
44 content = '''<style>
45 .lineno {{
46     color: {};
47     margin-right: 5px;
48 }}
49 {}</style>'''.format('#'+str(int( st.background_color[1:])+303030), myformat.get_style_defs('.highlight'))
50 #rule the style
51 
52 content += highlight(code, get_lexer_for_filename(sys.argv[1]), myformat)
53 #add the highlighted code
54 
55 
56 file.write(content)
57 file.close()


比起上次用原來的指令,所產生出來的html檔,這次不需要再多做一些修正,可以將完整的內容直接複製貼上,相對起來大概是有比較方便吧 :) 這篇就先到此吧~

下次再繼續PO心得
最後來首音樂吧



2014年5月4日 星期日

read open source pygments 1

pygments 簡單說是一個syntax highlighter,他支援的語言十分之多,最近會突然想閱讀他源碼,其實有部分原因是想開始練習閱讀open source,畢竟為了我另外一個project,我想,閱讀open source的能力,勢必要有所上升才行 :)

另外原因是,之前有朋友問,怎麼在blogger上面hightlight code,關於這個,我之前都是用這個 ,這是based on javascript的,所以必須要在blogger的後端,加入一些連結,而且每次要寫code時,都必須把code包覆在<pre brush:xx></pre>裡面,才會有效果出現,其實就以寫blog而言,真的有那麼一點不順就是了 :(

不過朋友說他做了設定後,卻沒有效果出來,好吧,我人沒在他旁邊,所以也不知道問題出在哪,所以最近在想乾脆用用pygments將code highlight後output成html格式,再將它PO在blog上面,這樣就比較簡單了 :) 於是我的閱讀之旅就這樣開始拉,雖然僅能利用假日來讀 :(


我想,首先就從package的 __init__.py來開始看,應該是沒錯的,如上圖所示,它做了一些最基本的說明 "pygments 是一個用python寫的語法高亮器 bla bla bla" 以下省略 :p

這邊特別說明一下 __all__ 這個東西的效用,其實看官網 的說明,就大概知道了,當你用 from pygments import *,如果你有定義 __all__ ,那它只會import 在all裡面的那些東東而已,沒定義的話,就會看你的 __init__.py 裡面有什麼就 import 啥,比如上面有import sys,那它也會跟著import sys,好啦轉回正題~

這個檔案的結構也滿簡單的,highlight這個function用到了 lex 和 format,另外當__init__.py被當成執行入口時,它會import cmdline.py,所以接下來我要去讀的package和檔案其實也滿明顯的,接下來我是選擇先閱讀cmdline.py,其實從名字一看也知道這是關於在cmd中如何進行操作的檔案,看完這檔案,應該就知道怎麼使用 pygments了,畢竟先懂得怎麼使用,然後再試著修改,我想這應該是不錯的順序吧 :p,另外也不用再去看官方docments的操作說明 :)


看吧,果然有使用方法,不過這檔案用到挺多東西的,其中它用到了兩個standard module,

1. getopt
2. textwrap

python果然真的是太方便了,光是內建就有一堆好用的東西,雖然這些自己寫一個也是OK拉:p

看了官方說明, getopt 主要是一個 sys.argv的parser, textwrap總之,方便文字格式處理的module,詳細不多說,畢竟這不是這篇文章的重點,且官方有不少說明,或者更有興趣直接看textwrap.py和getopt.py也是可以的。

 看了一些code後,使用上並不難,比如想要對test.py輸出一個語法高亮格式為html的檔案,打這樣的指令就可以了

pygmentize -O full,style=emacs -o test.html test.py

基本上只要你有提供輸入輸出檔,pygments會自動判斷該檔案的語法,當然你也可以自己輸入參數去規定syntax就是了,另外關於-O 那邊的設置,可以找源碼中的formatters的html.py裡面有相關說明,不同的輸出格式,會有一些有不同的option,效果如下

1
2
3
4
5
a = [ x for x in range(10) ]
b = a[::2]

print(a)
print(b)


我是將輸出的html內容擷取並貼上來,而且還要做一些小處理,只是如果每次都要這樣其實也滿累的 :( 目前就先到這邊吧,下次在PO繼續閱讀的心得,並且想個解決辦法 :p









2014年4月27日 星期日

pygame framefork 持續進行中

這個project的進度實在沒辦法進行的很快,畢竟現在我只有每周末可以回家寫,但是每次回到家都想要休息,另外一部份的原因實在是因為我對於設計GUI框架,沒有太多的概念,所以目前也只是按照我個人的想法來包裝,因此隨時都會有改動,最近感覺每次回到家,都要重新看code回想一下自己當初的想法是啥 :) 這樣感覺有點麻煩,所以這篇主要是來記錄一下目前的想法和進度的 :)

目前寫到這樣的結構,其實我已經開始有點混亂了,目前我是寫在 imageCropper裡面,並沒有將這個framework分層,因為目前只是測驗性階段,加上python是動態語言,所以沒有將code分層是不會影響到效率的。

我寫了這個測試,發現我的包裝少了什麼東西,就會回去把包裝修改一下,目前我有很多疑慮,像是

1.寫個物件是處理座標系統的,因為發現了絕對座標和相對座標的問題。
2.寫個物件是來計算每個widget位置的安排,像是考慮到margin padding等問題。
3.widget是不是該包含update這樣的函式,可是如果在每次的gameloop都call update,這樣會不會有效率上的問題,這樣一直改來改去,想來想去,讓我有點不知道所措。
4.需不需要把widget的屬性,另外用json的方式來進行讀取和設置

實在是有太多太多地方需要思考,現階段可能就以先增加其他widget再說吧,像是listbox、scrollbar等,另外我也在想寫遊戲裡的GUI,有必要寫到那麼麻煩嗎? 我一直在思考這些事情,所以project進度也一直沒很大的進展,所以我在想要不要稍微停下來,讀一下別人的open source來擷取一些想法,只是我查了一下pygame gui 的相關open source,大致上只有pgu 感覺比較好一點,可是我又覺得它似乎有點太雜了,再加上似乎沒有在維護了,到最後我找了一個based on pyglet 的game gui,就是這個simplui ,他也是沒在維護了 :( 只是相對我覺得他比較lightweight,所以我決定從這個下手,因為可能比較好讀 :) 不過我沒什麼閱讀open source project的經驗,所以可能會讀比較久一點,才會有相關的心得就是了,而且萬一他用到比較有技巧的東西,那可能就要更久了 :)

目前大概要一段時間才會PO 跟 framework有關的文章了 :)





2014年4月12日 星期六

試著寫一個小小的framework for pygame

首先就來說說,最近逛著一些論壇,發現越來越多framework跑出來,尤其是關於javascript的,看著那些強大的framework,總覺得自己的程式實力,仍需要很大的努力 :(

有時候看著別人用framework寫出很多不錯的應用程式,我心裡常這麼想著我是否也該要試著玩玩看,像我用python寫程式,python其中的宗旨是do not repeat yourself,這想法的確沒錯,已開發產品來講確實是這樣,因為這樣開發速度才會快,畢竟從0~1是很辛苦的,也很耗時,所以我也碰過一些python的library,像是pygame、pyglet、pillow、pyqt...等,但是問我對他們有沒有完全熟悉,我只能說大概沒有吧,我並沒有把裡面所有個函式都碰過,甚至也沒有將他們記起來,我都是想到需要什麼功能,就去官方文件查查有沒有相關的功能,所以我很常邊查文件邊寫程式。

其實這樣讓我有點擔心,因為要是沒有網路這樣一來我就不是變的不會寫程式了嗎?? 因為沒有文件可以查...可是對於python這語言大概是不太用擔心,因為library本身就幾乎是源代碼,所以可以直接去看源碼,除非他將一些核心用成dll等藏起來,但是對於C++呢? 除非library本身自帶文件那就不用怕了,只看header file你也不會懂那是啥,除非它有寫註解 :P。

在此同時,大量framework充斥的現在,讓我開始擔心起一件事,那就是我們這些後人,或許懂得快速利用哪棵樹來乘涼,但是懂得種樹嗎? 現在已經越來越少人種樹了,因為耗時、耗力,甚至是因為你自己種的樹不見的比其他人好 :(

就以小弟學了演算法來講,光是排序的問題好了,別人的library,可是有顧慮到對於不同的compiler和不同的作業系統甚至是記憶體的情況也考量進去,並分別給予不同的優化,這些種種,不是光隨便看幾天的書就可以寫出來的東西,自己或許真的可以寫出來,但是要花多久時間呢? 但是相對的,用別人的library呢? 花個幾秒的時間,你就可以達到相同效果,的確是何樂而不為呢 :) 這也是為啥懂得用別人的library,也是一件很重要的事 :)

話雖如此我還是覺得身為一個programmer,必須要懂得種樹,要不然programmer還有什麼價值? 只是看看文件,call一下function,說難聽點誰不會啊? 所以我還是選擇一個比較不好走的路,就是每個東西,都玩玩看,不管是底層,還是高階的framework,也因此我決定先來個小小的嘗試,那就是為pygame這個library寫個小小的framework,由於pygame本身並沒有GUI,所以我決定來練練如何自己包裝出一個GUI framework,雖然這並不是多底層的技術,但是我覺得這可以訓練一下,如何讓自己寫出一個大程式且附有重用性,以前我寫過最多行的專案了不起也才快2000行,我想要讓自己有更大的進步,目前我心中也沒有把整個程式結構想出來,一來是因為我查了一下網路,並沒有發現關於設計GUI的程式結構概念,或許有的? 如果有的話,希望好心人可以告訴我一下(留個言),目前的結構是這樣的,我先實現幾個widget,label、button和一個可show image的widget,如下圖,當然我也有包裝了一下,事件處理迴圈,相關code可以看這邊


只是目前我覺得我寫的這些code,可能以後會有很大的改變,畢竟目前我是依照直覺去包裝,往後如果有了整體的想法,就會另外PO一篇來記錄一下 :)

2014年2月5日 星期三

pyqt designer 初體驗

放了一個年假,總算是有點心情來搞跟程式有關的東西了 ˙ˇ˙   這次呢~我是嘗試著把之前做的imageDisplayer改成dark theme,功能還是一樣並沒有增加就是了:P 我只能說QT,真是讓人越用越愛阿,真的是一個很強大的library,他支援很強大的客製widget的功能,從官方網站的文件說明即可體會到這點,想到以前我在嘗試著用windows api去寫一個小小的GUI程式就搞了很久,要查很多api文件,而且那時候我還不知道怎樣去custom widget呢,舉例來說,把scroll bar改成很漂亮這樣,而不是一個很普通的樣子,從我給的連結應該就可以看出我要說的是什麼意思了。

另外很特別的一點,QT他是使用css來改變GUI widget的外貌的,這點還真的是非常方便,從連結可以看到,你可以把scrollbar的上下箭頭改成其他圖案,或者是讓其不要顯示,上下箭頭的部分是這樣的 QScrollBar::up-arrow, QScrollBar::down-arrow 你只要設定他的background:none就會讓箭頭消失了,QScrollBar::handle則是中間那個長方形,你當然也是可以隨意改變他的樣子,剩下的我想官方說明已經非常清楚就不一一拿出來說嘴。

不過目前我也是第一次用qt designer來做介面,另外我在實作時發現他好像有BUG,照理說這種WYSWYG,成品做出來後應該是不會差太多才對,不過很神奇的是,我用了Gridlayout去安排位置,結果我的button和lineEdit的位置都跑掉了...一整個滿神奇的,我為了搞這個搞了很久一再重複拉來拉去(感覺不太好拉阿sigh),然後再把ui檔compile到py檔,最後再看成品的UI位置,結果怎麼調都沒辦法,就暫時放下來了:P 因為目的也算是達到了,只是想要體驗custom widget的功能而已。

QT實在還有太多地方可以去鑽研,只是目前還有八個月役期的我,最多也只能利用放假來寫code了~

不知道是否各位寫code時,也會利用音樂增加寫code的動力 :P

 

PS:太久沒碰,總是需要動力來幫助的

2014年1月2日 星期四

python wsgi 初體驗~

最近終於稍微將上進的心,慢慢抓回來了 : ) 所以特別來寫一篇,雖然質量可能沒說很好,但總也是一個進步,好了,回到正題,其實是最近開始對python的django 感到有點興趣 :P

我照著官方的教學,跟著一步一步慢慢做,在其中赫然發現,有個東西叫做wsgi,那時我突然覺得奇怪,怎麼這東西似曾相似,於是我果斷就搬出我最會的技能( google it ),哈哈,原來python標準模組裡就有這種東西,難怪會有熟悉的感覺,看過wiki介紹後,才知道這是python所定出的一個interface,wsgi千萬不要硬記阿! 看過他全名後就很容易記起來 web server gateway interface,簡言之,它只是一種application和server間溝通的介面這樣,想要看更加詳細的解說,可以看看這個 網站,說得滿詳細的。


跟往常一樣就先來個測試範例吧

from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
from wsgiref.validate import validator


def simple_app(environ, start_response):
    setup_testing_defaults(environ)

    status = '200 OK'
    headers = [('Content-type', 'text/html; charset=utf-8')]

    start_response(status, headers)

    ret = '<script>alert("hello world")</script>\n'.encode('utf-8')
    return [ret]


validator_app = validator(simple_app)

httpd = make_server('', 8000, validator_app)
print('serving on port 8000')
httpd.serve_forever()

其實關於我給的那個網站,就有對於參數詳細的說明,或者去官方網站看也會得到解釋的,所以在這我就不會特別說明了 :P (其實是有點懶XD

start_response就是將http的status和header傳給server,想當然爾,傳完了initial資訊,就要接著傳內容,這邊有個特別的地方那就是回傳的值(內容)! 似乎必須要是iterable的值,以我的python版本,如果只是單純回傳bytes物件會發生錯誤,因此我才會用list。再來就是python強大又好用的地方了,他很貼心,有做個小型Server可以讓我們測試結果,而不用再去安裝其他server,還要去做相關設定....。開啟localhost:8000後,就會跳出一個視窗上面寫著hello world囉~ 顯而易見的答案對吧。


另外關於ret你是可以改成讀取index.html檔案,然後encode後再回傳,這樣做的話,就更有server的FU吧 :) 我想django大概也是利用類似的做法,不過django也說了他是一個架構,而非server,所以自附的server僅供測試方便,要發佈網站時,還是用專業的server比較好。

2013年8月29日 星期四

pyqt image displayer

最近開始動手寫我之前講要實做的image displayer了,要學的東西太多,所以就晚了許多時刻才開始研究 :)
大體上動機是想要讓自己更加了解qt這個library,另外的目的就是windows預設的image displayer是無法看動態gif的,所以我打算自己做一個可以看動態gif的圖片檢視器,目前的進度是這樣,一個非常楊春的界面,還有功能 :(

介面架構:

大概就簡單如上面所示,只是toolbar的方面還要設計icon,所以目前就沒加上去,所以進度目前是50%吧 :) (或許更低),下面影片是展示目前的進度



稍後整理目前心得,一直查官方文件看的有夠累 :(
整理完再重新編輯這篇文章 :-S
目前使用的qt物件
QGraphicsScene
QGraphicsView
QgridLayout
QMainWindow
..
..
其實QT實在有太多東西了,讓我覺得其實要做同樣的東西,可以使用很多不同的元件去達成,為何最後我會選QGraphicsScene來顯示圖片,是因為他對2D圖片顯示似乎是比較快的,當然你也可以用QLabel來達成,只是我想效率可能就會比較低吧? 沒實際比較過也不清楚 ,而且QGraphicsView可以控制你觀看QGraphicsScene的視角,像是旋轉阿~有的沒的,所以在種種因素考慮下才使用這兩個。

另外QT其實有提供designer讓你可以用拖曳的方式來做GUI界面,滿方便的:) 只是我想要先嘗試一下hard coding的感覺而已

要看源碼的話,我有放在github上面點此,最近才開使用 :)


2013年8月22日 星期四

python 分析程式效率

這是意外發現到的python standard module裡面有個較做profile.py,這東西實在是異常好用阿! 由於上次我用python寫粒子碰撞,我發現當我用到150個particle互相碰撞時,fps就開始降了,於是我開始想著要怎麼提升效率,我在論壇上面發問,結果就有人給我這個分析的方法,實在是很感謝那個人:) python又讓我驚豔了一次。  首先就來看看這張結果圖吧


非常詳細的分析,這個模組的使用方法其實很簡單,官方網站 隨便看都懂,在cmd裡面打cProfile.py -o outputfile.txt youfile.py,這麼一來就會將分析結果存在outputfile.txt中,cProfile.py是用C實現的比起profile.py純python實現,來的有效率。
然後呢要查看這個結果時再用 pstats 這模組

p = pstats.Stats('outputfile.txt')
p.sort_stats('time').print_stats(10)

經過這兩行就會,將結果過濾成以total time來排序並取前10名,如這張圖表示,拖累效率的地方在於collision這function,其實前幾名都把矛頭指向一個地方,那就是我之前寫的vector:(
果然用python來直接寫數學運算是比較沒效率的。

本來的想法是想用numpy來試試看,可是看過官方document後,我覺得numpy是對於數組的運算才有比較大的效率差別,也就是array的運算,因此決定乾脆用C++來寫一個python版的vector :) 不過這得稍微花時間來研究一下了 :)

2013年8月16日 星期五

初嘗pyqt

上次用python+PIL來進行將動畫GIF檔給分離出一張一張的圖片,結果不是很讓人滿意,因為PIL對於GIF檔的支援不是很好,因此有部分圖片顏色會有問題,關於當tkinter+PIL碰上gif的時候變的有多suck,看我另外一篇就知道了:(  所以我決定使用pyqt來測試看看。如下面這張animated gif





經過我寫的程式後,分離出來的結果


哈!真是完美,qt就是好用阿,只是目前我還沒有弄熟pyqt,只知道其實它不只是一個gui library,它包括的範疇可真是廣到不行阿,網路、多媒體阿,還有很多很多,目前還沒辦法寫出太多心得,對它還沒特別研究 :) 另外,它官方的文件寫得滿詳細的

img = QImageReader('D:/My Pictures/2.gif')
for i in range(img.imageCount()):
    img.read().save('{}.png'.format(i))
    img.jumpToNextImage()

我僅僅是看官方文件就輕鬆寫出將animated gif分離出來的code了如上面這樣簡單幾行。
對於教學的話,我目前沒有看到哪個教學是不錯的,如果有發現的話就另外補上 :)

2013年8月13日 星期二

python 物理模擬2

上次介紹過vector後,這篇就來講講我的碰撞是怎麼寫的,目前經過測試後我的計算應該是正確的,因為我計算了動能(k),的確有守恆,除了當我用滑鼠點物體的時候,一但點下去會讓物體的速度歸0,並且可以拖曳給予速度,如下影片所示。

話說有什麼軟體是錄製的時候比較不會LAG的嗎? 我用的這個軟體錄起來好像沒說那麼順暢 :(。 回到正題,這邊我是假設兩球碰撞後是沒有旋轉的,因此不考慮進去進入我碰撞程式碼前,先給個概念。
上面要表達的是,一顆移動速度V的球,撞上靜止的球,另外當兩個球體碰撞時,在那個瞬間會有個impulse,且這個imulse方向是平行於連心線的,因此碰撞後會改變得動量也只有平行於連心線的動量,所以在此就把速度向量V拆成平行於連心線(vcos)和垂直於連心線這(vsin)兩種,之後一切就好搞啦 :) 之前說得好像很難,在這邊我要說那時候我腦筋打結阿啊!! 思考不順暢 :( 回到正題,接下來只要把vcos代入,高中教的二維碰撞公式,再將得到的結果與vsin合併,最後這就是答案 :) 這邊我就不教怎麼導出公式了,有興趣的就自己去查囉。
def collision(self, obj):
        '''
            2d collision -- split in two part
            first -- v parallel to the line connected the two circle center

            second -- v vertical to the line connected the two circle center 
            inelastic case, both will has the same velocity....

            v1' = (m1-m2)v1/(m1+m2) + 2 m2v2/(m1+m2) 

            v2' = (2m1)v1/(m1+m2) + (m2-m1)v2/(m1+m2)

        '''
        if self.boundCircle.isCollision(obj.boundCircle): #compare two circle

            o1x, o1y = self.boundCircle.pos.point
            o2x, o2y = obj.boundCircle.pos.point
            p1 = Vector(o2x-o1x, o2y-o1y)

            v1, v2 = self.velocity, obj.velocity
            v1L, v2L = v1.length(), v2.length()

            try:
                rad1 = math.acos( v1.dot(p1)/ (p1.length()*v1L) )
            except:
                rad1 = 0
            try:
                rad2 = math.acos( v2.dot(-p1)/ (p1.length()*v2L) )
            except:
                rad2 = 0
            
                
            v1 = p1.normalize()*v1L*math.cos(rad1)
            v2 = -p1.normalize()*v2L*math.cos(rad2)

            
            m1, m2 = self.mass, obj.mass

            v1f = (m1-m2)*v1/(m1+m2) + 2*m2*v2/(m1+m2) 
            v2f = 2*m1*v1/(m1+m2) + (m2-m1)*v2/(m1+m2)
            

            self.velocity = v1f + (self.velocity-v1)
            obj.velocity = v2f + (obj.velocity-v2)

目前這碰撞只是兩個圓體,我還在想,要是圓體碰撞正方體,要怎麼去計算碰撞後的情形,還要若要考慮旋轉,勢必要改寫這個碰撞公式,必須再花點時間來研究 :) 但是其實如果不想浪費時間在自己寫這些有的沒的,到是可以用別人寫好的物理模擬,目前聽過的有box2d和bullet這兩種,以供參考。

2013年8月9日 星期五

python 物理模擬

最近心血來潮,想要自己實做一些物理方面的模擬,一般來講應該都是從2D方面開始,畢竟3D的世界有很多數學概念,相較起來2D的數學概念比較不吃重,話雖如此,只是果然天算不如人算阿 :(


一開始一路過關斬將,直到我開始寫碰撞的時候,一路上荊棘滿佈,頭一次寫程式寫到有種快要發飆的感覺 :) 我只能說我的數學,大學上學的我都忘了,剩下高中程度,若是一維碰撞那到是簡單的很,公式也能輕易推算出來,但是沒想到只是從1D轉到2D,整件事情就天差地遠阿,到目前為止,我也只是寫出個勉強貼近2D碰撞的模擬。

上面這影片就是我目前寫出來的樣子,獻醜了 :) 這篇就來貼一下,我的2D運算基本工具,基本上我是自己實做了一個vector的class,讓它具有一些特性
  • normalize
  • reflect
等等。
class Vector:

    def __init__(self, x=0.0, y=0.0):
        self.x = x
        self.y = y

    def __neg__(self):
        ''' ex. -Vector(2,2) -> Vector(-2,-2)'''
        return Vector(-self.x, -self.y)

    # def __del__(self):
    #   print('vector {} is delete'.format(self.point))

    def __getitem__(self, value):
        '''return a integer due to the coordinate in pygame is integer
        '''
        return int(self.__dict__[value])

    def __setitem__(self, index, value):
        self.__dict__[index] = value

    def __add__(self, rhs):
        return Vector(self.x+rhs.x, self.y+rhs.y)

    def __truediv__(self, rhs):
        if isinstance(rhs, Vector):
            raise ValueError
        return Vector(self.x/rhs, self.y/rhs)

    def __mul__(self, rhs):
        ''' rhs is a pure num '''
        if isinstance(rhs, Vector):
            raise ValueError
        return Vector(self.x*rhs, self.y*rhs)

    def __rmul__(self, lhs):
        return Vector(self.x*lhs, self.y*lhs)

    def __sub__(self, rhs):
        return Vector(self.x-rhs.x, self.y-rhs.y)

    def __imul__(self, rhs):
        '''
            *= equals to assign and inplace calculation
            ex. a *= b --> a = operator.imul(a, b)
        '''
        if isinstance(rhs, Vector):
            raise ValueError
        self.x *= rhs
        self.y *= rhs
        return self

    def __itruediv__(self, rhs):
        if isinstance(rhs, Vector):
            raise ValueError
        self.x /= rhs
        self.y /= rhs
        return self

    def __iadd__(self, rhs):
        self.x += rhs.x
        self.y += rhs.y
        return self

    def __isub__(self, rhs):
        self.x -= rhs.x
        self.y -= rhs.y
        return self

    def __str__(self):
        return 'vector x ={} y={}'.format(self.x, self.y)

    @property
    def point(self):
        return (int(self.x), int(self.y))

    @point.setter
    def point(self, value):
        self.x = value[0]
        self.y = value[1]

    @property
    def angle(self):
        ''' return radian'''
        return math.atan(self.y/self.x)


    def reflect(self, normal):
        '''
            I <  |---->normal
               \ | /
             ___\|/____

             2*(-I.dot(normal)) -- scalar
        '''
        I = self
        self = (2*(-I.dot(normal))*normal) + I
        return self

    def normalVector(self):
        ''' return the normal vector of self'''
        l = self.length()
        angle = self.angle + math.pi/2
        return Vector(l*math.cos(angle), l*math.sin(angle))

    def normalize(self):
        try:
            length = self.length()
            return Vector(self.x/length, self.y/length)
        except:
            return self


    def rotate(self, radius):
        newx = self.x*math.cos(radius) - self.y*math.sin(radius)
        newy = self.x*math.sin(radius) + self.y*math.cos(radius)
        return Vector(newx, newy)

    def length(self):
        return math.sqrt(self.x**2 + self.y**2)

    def dot(self, v2):
        if isinstance(v2, Vector):
            return self.x*v2.x + self.y*v2.y
        else:
            raise ValueError

關於python的operator我就不說明了,有興趣的就看看這個吧

裡面有些function的意思在這邊說明一下
  1. dot: return 兩向量 內積
  2. rotate: return 旋轉 XX radus後的向量
  3. normalize: 不知道怎翻耶,應該是單位向量這樣
  4. normalVector: return 相對於自己的法向量
  5. reflect: 給予法向量後,計算出反射後的向量
雖然看起來自己另外做一個vector好像有點多餘,只是我覺得這樣對於往後的coding會帶來一些方便性,另外比起用一些 list來代替vector,可讀性也比較高。


等我二維碰撞用出一個更加貼近真實的,我在PO心得,目前到這邊為止 :)

2013年7月28日 星期日

python tkinter 匯率換算器

其實我最近正在學pyqt,然後看到有個例子是匯率換算器,所以無聊之下換成用tkinter來寫寫看code如下

def test2():
    import tkinter
    from tkinter import ttk

    rates = {}

    def getdata():
        nonlocal rates
        try:
            date = 'unknown'
            fh = request.urlopen("http://www.bankofcanada.ca/en/markets/csv/exchange_eng.csv")
            #always remember that in python3 this function returns byte object!!
            for line in fh:
                line = line.decode('utf-8')# so need to decode
                if not line or line.startswith(('#', "Closing ")):
                    continue
                fields = line.split(',')
                if line.startswith("Date "):
                    date = fields[-1]
                else:
                    try:
                        value = float(fields[-1])
                        rates[fields[0]] = value
                    except ValueError:
                        pass
            return "Exchange Rates Date: "+ date
        except Exception as e:
            return "Failed to downloads:\n%s" % e

    def show(event):
        try:
            num = float(entry.get())
        except:
            pass

        fromCur = float(rates[fromCombo.get()])
        toCur = float(rates[toCombo.get()])
        result['text'] = fromCur/toCur * num


    root = tkinter.Tk()

    dateLabel = ttk.Label(root, text=getdata())
    listVar = sorted(rates.keys())


    fromCombo = ttk.Combobox(root, values=listVar, state='readonly')
    fromCombo.current(0)
    toCombo = ttk.Combobox(root, values=listVar, state='readonly')
    toCombo.current(0)

    entry = tkinter.Entry(root)
    result = ttk.Label(root, text='...')

    calculate = ttk.Button(root, text='calculate')
    calculate.bind('<Button-1>', show)


    dateLabel.grid(column=0, row=0, columnspan=2, padx=5)
    fromCombo.grid(column= 0, row=1, padx=10, pady=5)
    toCombo.grid(column=0, row=2, padx=10, pady=5)
    entry.grid(column=1, row=1, padx=10, pady=5)
    result.grid(column=1, row=2, padx=10, pady=5)
    calculate.grid(column=0, row=3, columnspan=2)
    
    root.mainloop()


if __name__ == '__main__':
    test2()
由於只是嘗試寫寫所以就沒有特別去想結構了,用了不怎麼好看的寫法 :)

只是有個地方滿好奇的,我用這匯率去算跟台銀給的匯率計算器得出的結果不一樣(汗,不過其實我也不太懂匯率是怎麼得出來的,所以用這個程式得出來的結果看看就好,想要知道目前的匯率還是去台銀官網看吧 :) 或者就是把這篇code所抓取的csv改成台銀的 :)

2013年7月24日 星期三

python partial function

    今天到是第一次聽到這個名詞,好奇之下去孤狗了一下,沒想到其實也沒有什麼特別的,應該很多人自己在coding的時候都用過了,這種東西就像design pattern一樣,是一種大家可能都會在不知不覺中使用出來的模式,因為常被使用而且有不錯的用途,因此取個名字記錄下來罷了,首先就來看看一段code

import functools

def f(base=1, pow=1):
    return base**pow

f3 = functools.partial(f, 3)
print(f3(3)) 


    看到這裡是否看出什麼了呢? 沒錯partial function其實也就只是將原本的function多做一層包裝,因為可能在某些情況下,你需要一直打出這些函式,每次都要一直傳參數,以上面為例如果你需要一直call以3基底各種次方數的函式,比起call f(...),call f3(x)簡單多了不是嗎?所謂的partial function就大概是為此而在的。

    functools.partial 第一個參數是你原本的fucntion,接著就是放進函式參數囉,像這邊f3就是指base=3,之後呢我在CALL,f3的時候再給予pow值 ,其實我也不知道functools這模組存在的理由 :) 可能是我幾乎沒再用的關係啦,或許他有好用的一面,至少他幫你做了一些包裝,只是我還是無聊做了一個實現


def partial(f, *arg, **karg):
    def innerF(*iarg, **ikarg):
        ikarg.update(karg)
        return f(*(arg+iarg),**ikarg)
    return innerF

我不太確定是否functools.partial是否是這樣實現的,只是至少我下面這完整的code沒問題 :)

import functools

def partial(f, *arg, **karg):
    def innerF(*iarg, **ikarg):
        ikarg.update(karg)
        return f(*(arg+iarg),**ikarg)
    return innerF


def f(base=1, pow=1):
    return base**pow

f2 = partial(f, 2)
f3 = functools.partial(f, 3)

print(f2(3),f3(3))


2013年7月23日 星期二

python tkinter+PIL

有鑑於Windows內建的圖片預覽器沒辦法看animated gif,因此我突然想要自己實做一個,想當然爾是用python開發方便 :) 只是,果真是人算不如天算阿!! 由於tkinter本身並沒辦法去讀取png、jpg等格式的檔案,所以我另外去裝了PIL。

哼哼,雖然我成功讀取那些檔案,但是 "animated gif" 仍然是一個很大的問題,相信我! 千萬不要用 PIL 去讀取animated gif :(  it's the worst gif-supported library I've ever seen. 我是有成功抓到每個frame出來,但是將每個frame存成圖後簡直悲劇,失真失得好慘阿....之後找了找我發現有人的解法是類似我寫的這樣..

img = Image.open(filename)
palette = img.getpalette()
seq = []
try:
    while True:
        img.putpalette(palette)
        new_im = Image.new("RGBA", img.size)
        new_im.paste(img)
        self.frame.append(new_im)
        img.seek(img.tell()+1)
except EOFError:
    #mission complete
    pass

for i in range(len(self.frame)):
    self.frame[i].save('{}.png'.format(i))

說是這是pil的bug,說啥每當seek到下一個frame時,會失去palette,因此要幫他補上去,只是阿...我這樣做了,就只有部分gif是OK的,有些仍然會有問題!! 我想可能要用其它的library來實做了吧 :(  這次的經歷給我了一句話那就是 PIL + GIF = TORTURE

寫這篇的目地,是不希望有下個人給我一樣在這邊耗這麼久,到頭來卻毫無收穫

2013年7月17日 星期三

python tkinter <簡單的滑鼠畫線>

真的好感慨啊!! 想想以前用windows api去寫相同的程式,就要多很多工夫,很多底層的東西都要自己實做,用別人幫你建構好的架構就是快速簡單阿 :)
from tkinter import *
from tkinter import ttk


def main():
    lasty, lastx = 0, 0

    def xy(event):
        nonlocal lasty, lastx
        lastx = event.x
        lasty = event.y

    def addLine(event):
        nonlocal lasty, lastx
        canvas.create_line((lastx, lasty, event.x, event.y))
        lasty = event.y
        lastx = event.x


    root = Tk()
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)

    canvas = Canvas(root)
    canvas.grid(column=0, row=0, sticky='nwes')
    canvas.bind('<Button-1>', xy)
    canvas.bind('<B1-Motion>', addLine)
    root.mainloop()


if __name__ == '__main__':
    main()

這樣短短的code就實做出來了。 成果如圖案所示(掩面,用滑鼠寫字好難 :( )



其實這次會PO這篇,是想要做個記錄:) 頭一次用到nonlocal = =,這是用來取的最近的上層scope的變數,因此我在 xy函式裡面寫了 nonlocal lastx, lasty 是為了取得 main函式的 lastx, lasty的reference,讓我可以隨意改變他們的值

2013年7月13日 星期六

use ctypes to call windows api

繼續之前使用ctypes實做Windows data type,這次要做的是使用windows api,來做一個console interface的聊天室,此篇僅先做出介面的樣子



import ctypes
from ctypes import wintypes
from ctypes.wintypes import *
import msvcrt
import sys

#-----------------debug use----------------------
import inspect

def myDebugMsg(msg=''):
    print('{}   at:{}'.format(msg, inspect.stack()[1][1:3]))

def pause():
    while True:
      if msvcrt.kbhit():
         break
#------------------------------------------------


#--------------------------------------------------
# use ctypes to create a windows data type
class Char(ctypes.Union):
    _fields_ = [("UnicodeChar",WCHAR),
                ("AsciiChar", CHAR)]

class CHAR_INFO(ctypes.Structure):
    _anonymous_ = ("Char",)
    _fields_ = [("Char", Char),
                ("Attributes", WORD)]


class CONSOLE_CURSOR_INFO(ctypes.Structure):
    _fields_ = [('dwSize', DWORD),
                ('bVisible', BOOL)]


PCHAR_INFO = ctypes.POINTER(CHAR_INFO)
COORD = wintypes._COORD
#---------------------------------------------------


class dllLoader:
    '''
        load the dll written by c and call them for use 
    '''
    def __init__(self):
        self.mKernel32 = ctypes.WinDLL('Kernel32.dll')
        self.mUser32 = ctypes.WinDLL('User32.dll')

    def getKernel32(self):
        return self.mKernel32

    def getUser32(self):
        return self.mUser32

    Kernel32 = property(getKernel32)
    User32 = property(getUser32)


dll = dllLoader()

#-------------------------------------------------------

上面這些code是先將我會用到的dll檔先load好和windows api data type實做出來,另外還有方便我用來debug的函式,接下來就是用來實做介面的code

class consoleBackBuffer:
    def __init__(self, w, h):
        self.mstdout = dll.Kernel32.CreateConsoleScreenBuffer(
                        0x80000000|0x40000000, #generic read and write
                        0x00000001|0x00000002,
                        None,
                        1, #CONSOLE_TEXTMODE_BUFFER defined in winbase.h
                        None)
        if self.mstdout == HANDLE(-1):
            myDebugMsg('CreateConsoleScreenBuffer failed')


        self.cursorInfo = CONSOLE_CURSOR_INFO()
        self.cursorInfo.dwSize = 25


        self.coordBufSize = COORD()
        self.coordBufSize.X = w
        self.coordBufSize.Y = h

        self.coordBufCoord = COORD()
        self.coordBufCoord.X = 0
        self.coordBufCoord.y = 0


        self.readRgn = SMALL_RECT()
        self.readRgn.Top = 0
        self.readRgn.Left = 0
        self.readRgn.Right = w-1
        self.readRgn.Bottom = h-1


        self.writeRgn = SMALL_RECT()
        self.writeRgn.Top = 0
        self.writeRgn.Left = 0
        self.writeRgn.Right = 79
        self.writeRgn.Bottom = 24

        self.actuallyWritten = DWORD() # used when writeconsole called

        self.setCursorVisibility() # by default, set cursor invisible

    def setCursorVisibility(self, flag = False):
        self.cursorInfo.bVisible = flag
        dll.Kernel32.SetConsoleCursorInfo(self.mstdout, ctypes.byref(self.cursorInfo))

    def toggleActiveConsole(self, stdout=None):
        if stdout != None:
            dll.Kernel32.SetConsoleActiveScreenBuffer(stdout)
        else:
            dll.Kernel32.SetConsoleActiveScreenBuffer(self.mstdout)

    def getHandle(self):
        return self.mstdout

    def set_color(self, color):
        dll.Kernel32.SetConsoleTextAttribute( self.mstdout, color )


    def gotoxy(self, x, y, stdout=None):
        coord = COORD(x, y)
        if stdout == None:
            dll.Kernel32.SetConsoleCursorPosition( self.mstdout, coord)
        else:
            dll.Kernel32.SetConsoleCursorPosition( stdout, coord)

    def setWriteSrc(self, x, y):
        self.writeRgn.Top = y
        self.writeRgn.Left = x
        self.writeRgn.Right = x+self.coordBufSize.X-1
        self.writeRgn.Bottom = y+self.coordBufSize.Y-1

    def write(self, msg):
        while len(msg)>self.coordBufSize.X:
            
            tempMsg = msg[:self.coordBufSize.X-1]+'\n'
            msg = msg[self.coordBufSize.X-1:]
        
            success = dll.Kernel32.WriteConsoleW(
                self.mstdout,
                tempMsg,
                DWORD(len(tempMsg)),
                ctypes.byref(self.actuallyWritten),
                None)

            if success == 0:
                myDebugMsg('WriteConsoleW failed')

        if '\n' not in msg:
            msg = msg+'\n'

        if len(msg) != 0:
            success = dll.Kernel32.WriteConsoleW(
                    self.mstdout,
                    msg,
                    DWORD(len(msg)),
                    ctypes.byref(self.actuallyWritten),
                    None)

            if success == 0:
                myDebugMsg('WriteConsoleW failed')

    def present(self, mainBuffer):
        chiBuffer = (CHAR_INFO*(self.coordBufSize.X*self.coordBufSize.Y))()

        success = dll.Kernel32.ReadConsoleOutputW(
                self.mstdout,
                ctypes.byref(chiBuffer),
                self.coordBufSize,
                self.coordBufCoord,
                ctypes.byref(self.readRgn)
                )

        if success == 0:
            myDebugMsg('ReadConsoleOutputW failed')

        success = dll.Kernel32.WriteConsoleOutputW(
                mainBuffer,
                ctypes.byref(chiBuffer),
                self.coordBufSize,
                self.coordBufCoord,
                ctypes.byref(self.writeRgn))

        if success == 0:
            myDebugMsg('WriteConsoleOutput failed')

我會實做一個console buffer的原因是,我想要用double buffer,畢竟畫面需要常常更新,為了不讓畫面有閃爍,因此使用這個技術。題外話,其實感覺滿新鮮的 :) 因為我以前用過double buffer是在使用 windows api的gdi才用過,果然在CLI裡也有 :) 親自實做的感覺,其實滿有成就感的,接下來就是做一些 widget出來,只是我對於GUI的元件該有怎樣的包裝,我也不清楚,因此只是隨意的做做而已 :(

class widget:
    def __init__(self, sx, sy, w, h):
        self.console = consoleBackBuffer(w, h)
        self.console.setWriteSrc(sx, sy)
        self.gx = sx
        self.gy = sy
        #global position
        self.w = w
        self.h = h
        self.mTitle = 'no name'
        self.content = []

    def getConsole(self):
        return self.console

    def getContent(self):
        return self.content[:]

    def setContent(self, content):
        self.content = content[:]


    def setTitle(self, title):
        self.mTitle = title

    def getTitle(self):
        return self.mTitle

    title = property(getTitle, setTitle)

    def addContent(self, s):
        if len(s) >= self.w-3:
            self.content.append(s[:self.w-3])
            self.content.append(s[self.w-3:])
        else:
            self.content.append(s)

    def delContent(self, s):
        self.content.remove(s)

    def display(self, stdout):
        self.console.present(stdout)


class usermenu(widget):
    '''
        show user list
    '''
    def __init__(self, sx, sy, w, h):
        super().__init__(sx, sy, w, h)

    def update(self):
        #draw outline
        border = '|'+'-'*(self.w-2)+'|'
        emptyLine = '|'+' '*(self.w-2)+'|'
        for y in range(self.h):
            if y in (0, 2, self.h-1):
                self.console.write(border)
            else:
                self.console.write(emptyLine)

        #draw title and user list
        tx = int( (self.w - len(self.title))/2 )
        self.console.gotoxy(tx, 1)
        self.console.write(self.title)


        for i in range(len(self.content)):
            self.console.gotoxy(2, 3+i)
            self.console.write(self.content[i])
        self.console.gotoxy(0, 0)


class msgroom(widget):
    def __init__(self, sx, sy, w, h):
        super().__init__(sx, sy, w, h)
        self.scroll = 0
        self.start = 0

    def scrollContent(self, offset):
        self.scroll += offset
        if self.scroll > 0:
            self.scroll = 0

    def detectPageUpAndDown(self):
        pageUP = dll.User32.GetAsyncKeyState(0x21)
        pageDown = dll.User32.GetAsyncKeyState(0x22)

        if pageUP != 0:
            self.scrollContent(-2)
        if pageDown != 0:
            self.scrollContent(2)

    def update(self):
        self.detectPageUpAndDown()
        border = '|'+'-'*(self.w-2)+'|'
        emptyLine = '|'+' '*(self.w-2)+'|'

        for y in range(self.h):
            if y in (0, 2, self.h-1):
                self.console.write(border)
            else:
                self.console.write(emptyLine)
        #draw title and content
        tx = int( (self.w - len(self.title))/2 )
        self.console.gotoxy(tx, 1)
        self.console.write(self.title)

        index = len(self.content)-(self.h-4)+self.scroll
        self.start = 0 if index < 0 else index

        obj = self.content if len(self.content) < self.h-4 else self.content[self.start : self.start+self.h-4]
        # wow, slice in python seems to automatically check the index if it is out of range
        # ex. lst = [1,2,3,4,5]
        # lst[-6:] no error produce

        for i in range(len(obj)):
            self.console.gotoxy(1, 3+i)
            self.console.write(obj[i])
        self.console.gotoxy(0, 0)



class inputLabel(widget):
    def __init__(self, sx, sy, w, h):
        super().__init__(sx, sy, w, h)

    def update(self):
        border = '|'+'-'*(self.w-2)+'|'
        emptyLine = '|'+' '*(self.w-2)+'|'

        for y in range(self.h):
            if y in (0, 2, self.h-1):
                self.console.write(border)
            else:
                self.console.write(emptyLine)

        tx = int( (self.w - 8))
        self.console.gotoxy(tx, 0)
        self.console.write('|')
        self.console.gotoxy(tx, 2)
        self.console.write('|')
        self.console.gotoxy(tx, 1)
        self.console.write('|submit')
        self.console.gotoxy(0, 0)
好了到這邊為止任務就完成了,想當然爾,要寫個code來測試,自己寫出來的code是不是跟自己想的效果一樣
def test():
    ''' 
        like this?
        in windows, the size of console is 80X25 by default 

        setting: 
            2 space for left and rigth edge
            one space or \n for each panel

            chatroom: 15 height, 55 width
            user list: 16 height, 15 width
            submit: 71 width, height 3
        |-------------------------------------------------------------| |-----------|
        |                        chatroom                             | | user list |
        |-------------------------------------------------------------| |-----------|
        |name2: hello, you                                            | |df         |
        |name1: yo                                                    | |yeah       |
        |                                                             | |one        |
        |                                                             | |           |
        |                                                             | |           |
        |-------------------------------------------------------------| |-----------|

        |-------------------------------------------------------------------|-------|
        |  yo, hahaha                                                       |submit |
        |-------------------------------------------------------------------|-------|

    '''
    from threading import Timer, Thread


    hstdout = dll.Kernel32.GetStdHandle(DWORD(-11))

    if(hstdout == HANDLE(-1)):
        print('create buffer failed')

    backBuffer = consoleBackBuffer(80, 25)

    userpanel = usermenu(56, 0, 15, 17)
    userpanel.title = 'user list'
    userpanel.addContent('heyhey')
    userpanel.addContent('haha')
    userpanel.update()
    userpanel.display(backBuffer.getHandle())

    chatroom = msgroom(0, 0, 55, 17)
    chatroom.title = 'chat room'
    chatroom.addContent("eric: I'm so happy")
    chatroom.update()
    chatroom.display(backBuffer.getHandle())

    inLabel = inputLabel(0, 18, 71, 3)
    inLabel.update()
    inLabel.display(backBuffer.getHandle())

    backBuffer.present(hstdout)
    input()



if __name__ == '__main__':
    test()

結果跑出來的,如同我想像 happy :)
其實後來我有使用socket,thread模組,加上這篇寫的code實做出一個多人聊天室,只是感覺鳥鳥的,有點不想PO出來 :( ,最近是打算寫出一個聊天軟體是GUI介面的,所以要先來研究tkinter,希望能夠成功做出來。

2013年7月12日 星期五

tkinter 雜記

最近想要用python寫個GUI的聊天軟體,於是在網路上查了一些資料,似乎pyQT滿受歡迎的,只是本人我一直以來有個毛病,就是不喜歡在電腦裝東裝西的 :) 因此決定使用python標準庫裡面的tkinter,只是剛開始不知從何下手的我,搜尋了很久,發現似乎沒有個很全面的文件,都是零零碎碎的,於是記錄一下目前發現的幾個reference

http://thinkingtkinter.sourceforge.net/
http://infohost.nmt.edu/tcc/help/pubs/tkinter.pdf
http://www.tkdocs.com/
http://docs.python.org/3.2/library/tkinter.html

大概要花一些時間來研究一下,才能有心得。
總之就先來個無聊的hello world吧 :-S
剛剛發現其實python官網文件好像就很不錯了,已補上,發現什麼的話,此篇隨時會更新 :)

from tkinter import *
from tkinter import ttk
from tkinter.ttk import *

def main():
    root = Tk()
    style = ttk.Style().configure('TLabel', foreground='black', background='green')
    ttk.Label(root, text='Hello World').grid()
    root.mainloop()

if __name__ == '__main__':
    main()

已發現 http://www.tcl.tk/man/tcl8.5/TkCmd/grid.htm 這是tcl/tk的官方文件,從這裡就可以查到你所有想要的關於tkinter的reference。

#追加目前為止的心得,從網路上學了一些關於tkinter使用到的原理,也照著教學,寫了一個嘗試的code,如下
from tkinter import *
from tkinter import ttk



def main():
    root = Tk()
    
    content = ttk.Frame(root, padding=(3, 3, 12, 12))
    frame = ttk.Frame(content, borderwidth=7, relief='solid', width=200, height=100)
    namelbl = ttk.Label(content, text='Name')
    name = ttk.Entry(content)

    onevar = BooleanVar()
    twovar = BooleanVar()
    threevar = BooleanVar()

    onevar.set(True)
    twovar.set(False)
    threevar.set(True)

    one = ttk.Checkbutton(content, text='one', variable=onevar, onvalue=True)
    two = ttk.Checkbutton(content, text='two', variable=twovar, onvalue=True)
    three = ttk.Checkbutton(content, text='three', variable=threevar, onvalue=True)

    ok = ttk.Button(content, text='OK')
    cancel = ttk.Button(content, text='Cancel')

    content.grid(column=0, row=0, sticky=(N,S,E,W))
    frame.grid(column=0, row=0, columnspan=3, rowspan=2, sticky=(N,S,E,W))
    namelbl.grid(column=3, row=0, columnspan=2, sticky=(N,W), padx=5)
    name.grid(column=3, row=1, columnspan=2, sticky=(N,E,W), pady=5, padx=5)
    one.grid(column=0, row=3) # row=2 seems the same
    two.grid(column=1, row=3)
    three.grid(column=2, row=3)
    ok.grid(column=3, row=3)
    cancel.grid(column=4, row=3)

    # when the window resized, all widget will be resized, too
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    content.columnconfigure(0, weight=3)
    content.columnconfigure(1, weight=3)
    content.columnconfigure(2, weight=3)
    content.columnconfigure(3, weight=1)
    content.columnconfigure(4, weight=1)
    content.rowconfigure(1, weight=1)

    root.mainloop()



if __name__ == '__main__':
    main()

其實有稍微學過視窗程式設計的應該都知道,這類的都跑不掉有一個主迴圈,是用來處理event queue的,因此python tkinter也不例外 root.mainloop(),到目前為止,跟我以前用windows api寫的概念有所不同的是,tkinter他有所謂的 geometry management,是用來計算每個widget該放置的位置以及大小,geometry management 有三種
  1. place
  2. pack
  3. grid
從我看過的教學,pack是最早有的,它說功能強大(掩面 但是我不知道強在哪,place目前還沒用所以不清楚,最後grid是後來tkinter才出現的,但是它一出現後來很多人都用它,由此可見grid應該是比pack好用,因此我看的教學也是用grid居多,說穿grid頂多只是把視窗分成row和column這樣,方方格格,之後規定每個widget所在的row, column,以及它所跨越的row, column區間(ex. columnspan),這樣程式執行後,它就會自動計算每個widget的位置了~
至於columnconfigure and rowconfigure則是用來window resize的時候,它會讓裡面的元件,依照你規定的weight去放大( ex. weight=1 => 1 pixel to 1 pixel,  weight=3 => 1 pixel to 3 pixel)

2013年7月5日 星期五

python ctypes

最近做了小小的測試,使用python的ctypes來實做windows api的data type

ex.1
typedef struct _CHAR_INFO {
    union{
        WCHAR UnicodeChar;
        CHAR  AsciiChar;
    } Char;
    WORD  Attributes;
} CHAR_INFO, *PCHAR_INFO;


用ctypes實做如下先將名為Char的union實做出來

class Char(ctypes.Union):
    _fields_ = [("UnicodeChar",WCHAR),
                ("AsciiChar", CHAR)]
再來將CHAR_INFO整個structure實做出來

class CHAR_INFO(ctypes.Structure):
    _anonymous_ = ("Char",)
    _fields_ = [("Char", Char),
                ("Attributes", WORD)]
PCHAR_INFO = ctypes.POINTER(CHAR_INFO)

噹噹噹~mission complete
ex.2
typedef struct _CONSOLE_CURSOR_INFO {
    DWORD dwSize;
    BOOL  bVisible;
}CONSOLE_CURSOR_INFO, *PCONSOLE_CURSOR_INFO;
class CONSOLE_CURSOR_INFO(ctypes.Structure):
    _fields_ = [('dwSize', DWORD),
                ('bVisible', BOOL)] 
        其實ctypes這模組挺方便的,可以隨意的call用C寫出來的dll檔,如果只是想要用到一點點dll檔,我想自己寫就好了,不需要去用到其它模組,像是如果你要大量的用到windows api那就果斷用pywin32吧:),裡面可幫你寫好一堆的東西呢,畢竟這動作實在是很機械式,有別人幫你種樹,就去乘涼吧。
    
        當然用python去CALL用C寫的dll也有其他目的,通常是效率問題,把效率吃緊的地方給較低階的語言去實做,目前知道有個叫SWIG,可以將很多語言黏在一起,因為小弟也沒用過所以不敢多言