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月27日 星期二

git note

git要創造repository的方式有兩種
  1. git init
  2. git clone
第一種,就是你在cmd中,先移到你要的目錄然後輸入git init,就會產生.git的directory,裡面當然就是包含著一些有的沒的,用來確認檔案狀態阿,還有一些configuration,目前不會深入講解那些。

第二種,就是比如你在網路上看到一些open source比如像這個,你有興趣想要把它整個下載下來,這時候如同上面先移到你要的目錄,然後輸入 git clone https://github.com/django/django.git 這樣就會將整個repository下載下來。



圖案來源是前一篇所堤到的電子書裡面的 :) 目前正在閱讀中

git對於檔案有分為上面這幾種狀態
對於repository裡面的檔案狀態,你隨時可以輸入git status來查看

舉例:
剛開始一個空的repository,如果你輸入git status則會告訴你nothing to commit,意指沒有檔案被修改,接著你創了一個readme.txt,再輸入一次這指令,它就會告訴你你有untracked file: readme.txt,所以當你想要讓git開始track這個檔案的時候就得輸入 git add readme.txt,如此一來這個檔案就處於staged的狀態,再來如果你修改了readme.txt,這時候檔案就處於modified狀態,必須再輸入一次git add讓它再一次處於staged狀態。

為何要讓檔案處於staged狀態呢? 因為git commit這指令只會commit那些檔案處於staged狀態。其實這些指令還有很多詳細的用法,你只要在cmd中輸入git help就會跑出一堆git可以用的指令,然後在更進一步比如你想要知道git add有什麼其他用法,那就輸入git help add,就會有更詳細的解說。

另外你可以寫一個.gitignore檔案,在裡面寫你想要避免被add到staged的檔案像是
*.[o]這樣可以避免所有副檔名為o的檔案
為何會有這種東西的存在呢?因為有時候你不想一個檔案慢慢add吧這時候你可以輸入git add -A就會把所以在目錄下的檔案都add到staged,所以不想有個萬一的話,就寫的.gitignore檔吧


大致上git其實也沒什麼好要去理解的,我想只要把git內部如何運作稍微了解,指令什麼的只要查一查用一用就好了:)




2013年8月26日 星期一

start to use git





其實以前就有用過一點git,那時候的我覺得畢竟我很少寫大型的program,實在不怎麼需要用到version control這種工具,但是隨著日子久了,我發現從以前到現在的累積的code檔案數量也不少呢,只是這些檔案我也想要有個備份,所以我就放在網路上面的空間囉 :) 那些檔案大概是不太會再動了:) 但是為什麼今天我又要用git呢? 只是要做備份又何必要用到git? 沒錯! 純粹備份是不需要用到version control的,一般的存放空間即可。

但是有時候我突然在想如果我在不同的地方,想要繼續把某個code給完成,這時候我就得先想過,我曾經在某地方是否下載過,然後做了一些修改,最後是否有上傳呢? 如果沒有的話,那我現在寫的這些又該怎麼跟之前寫過的合併?



 嘿嘿,version control這種工具就有提供merge這種功能,可以把兩種不同版本的code給merge起來,你可以先把目前寫的上傳上去,然後在回去把之前修改過的也上傳上去,最後在merge,一切輕鬆,而且另外的情形是,萬一你修改了很多很多的code,然後你突然後悔了想要復原這些修改,又剛好你在寫之前沒備份,這時候git就方便拉,它有功能是可以讓你回到上一個版本的樣子呢。

version control是不錯用的工具,所以我想說乾脆以後如果要寫什麼program的話,乾脆就用git這version control的工具,放在github這樣,順便也可以當作備份呢 :)

只是git我目前也不怎麼熟悉,所以打算做一些做記錄,免得忘記一些指令該怎麼用 :)

這是官網 上面有免費的電子書,可以下載下來,想學git就去看吧

2013年8月23日 星期五

makefile note3

關於makefile的變數assign的方式,目前看到的大概有三種,詳細資料請看 這個

第一種 simple expand variable,是使用 := 這樣的operator
舉個例子

who := mark
act :=  $(who) say
who := alice
echo $(act)

將會是 mark say,:=這種assgn的operator,如同字面上所言簡單的擴展,他一遇到該行就直接擴展並不會去查他最後是什麼樣子,如果你想要結果是 alice say的話那麼就是要使用第二種方式。

第二種 recursive expand,是使用= 這樣的operator

who = mark
act = $(who) say
who = alice
echo $(act)

這樣一來結果就會是alice say

第三種是conditional variable assginment operator,是使用?=這樣的operator
who = mark
act = $(who) say
who ?= alice
echo $(act)
嘿嘿結果是 mark say喔~ 這個operator的作用是他會檢查該變數是否有值,有值的話就不會做assign的動作

關於make,基本上似乎看官方文件就很OK了,遇到不會的符號查查官方文件大概就有解,我是看gnu make的官方文件,畢竟mingw-make我找不到相關的文件,我想大概是mingw-make就是gnu make來的吧,所以兩者差異應該不大才是。

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月19日 星期一

makefile note 2

關於make的target和prerequisites是用dependency graph來做記錄的,所以像這樣的寫法

a: b.c c.c
a: d.c e.c
其實同等於
a: b.c c.c d.c e.c

他只是持續的修改dependency graph而已,但是如果是用double colon(例:a::b.c c.c)事情就會變得不一樣了,它會分別建立dependency graph,而不會append原來的。


上次說到target可以為檔名也可以不是對吧,不是檔名的target,在術語上名為phony target,其實如同字面上的意思,phony -> fake,假的target,為什麼叫做假的target? 因為這個target並非用來產生檔案的,他只是純粹執行一些shell指令,像是這樣

clean:
    rm -f *.o

如此這類的,但是呢! 當你要用來做這種事情的時候,請記得多加這一行 .phony: clean
會需要多加這行的原因是 make它是無法辨別target是要用來產生檔案或是純粹執行指令,所以要加.phony的宣告,讓make知道它是always out of date,舉個例子吧,如果今天你沒有加這行會發生什麼事呢?

當你同目錄下沒有檔案叫做clean的話那一切就會正常,always out of date,但~~~是~有的話呢!,此時你的target就會被認為是要產生檔案用的,由於clean的rule並沒有任何prerequisites所以always up to date,所以就不會被執行了。

另外make是支援自定義變數的,例如: 你宣告了 compile = g++ , 那麼當你要使用這變數時要用錢字號來取得,$(compile)這樣,make有所謂的 auto variable,詳情請見 這個
目前對這個沒太多研究,我想這個的目的大概是減少code的量,畢竟一直重複打同樣的字是一件很煩的事,像機器人的做的事就交給機器人來做吧 :)

2013年8月17日 星期六

makefile note

我一直以來其實都不怎麼想用makefile來進行編譯的工作 :( 因為實在是有太多好用的IDE了!哈哈,只是我最近常常用sublime text做為我的coding環境,但是只用它來寫python和網頁語言而已。

最近突然想到乾脆比較簡單的C++也在這環境寫好了:) 因此決定來親自碰一下這東西好了,這樣一來我只要自己寫一個subilme build的檔案,就可以按下ctrl+B進行編譯了。


如上所示!OK

 makefile的規則包括三個部份如下
  1. target
  2. prerequisite
  3. commands
target: prerequisite
[tab] commands  # [tab]表示字元\t而已

target:簡單說就是你所要建置的目標,不一定要為檔名
prerequisite:建置目標前,所需要的先決條件
commands:就是shell commands!! 記得開頭得有tab才行

ex:
cc.exe: cc.o
[tab] mingw32-g++ cc.o -o cc.exe 
cc.o: cc.cpp
[tab] mingw32-g++ cc.cpp -std=c++11 -c

然後當我在cmd中輸入mingw32-make,由於我沒給任何argument所以他會以預設得來執行,但是預設的是什麼呢? 答案就是開頭最前面的target。接下來make的分析規則大概是這樣的,先看target和prerequisite去找檔案,如果prerequisite有相關的rule存在那麼就會以他為優先去執行,另外如果prerequisite比target還要新(意思是像是進行修改),那麼就會rebuild該target。

所以,以範例為例一開始碰到cc.exe: cc.o,很好發現了cc.o有相關的rule,那就是cc.o: cc.cpp......那串,所以就會變成先優先執行這條 ,執行完後就會產生cc.o檔,然後就回去執行剛剛那條rule,最後就產生cc.exe這樣。

make是還有更多的用法的,目前這篇僅記錄最基本的法則和用法,不過印象中make不同的版本似乎會有他們獨特的用法就是了,我也不知道會不會用到那麼多 :)

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年8月5日 星期一

how to write a dll

dynamic link library(dll) 這是在windows上的稱呼,至於linux的稱呼是 shared library,沒記錯的話應該是這樣沒錯。最近想嘗試用python去call自己寫的dll檔,所以就開始學習如何產生dll檔。

 經過我的嘗試後,發現dll的使用方式有兩種
  1. load-time dynamic linking (個人喜歡稱 implicity load way
  2. run-time dynamic linking ( explicit load way

為何會這樣稱呼呢? 我想在稍後或許你就會了解了。
首先寫一個dll檔,它也是有所謂的entry point的,就像是用C++和C寫一個console介面的程式,main就是entry point,win32則是WinMain。
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
    return TRUE;
}
我對這個entry point實際上可以做哪些還不是很清楚,依照我看到的資料是說可以在這邊做一些資料的初始化和釋放,但是這個entry point可有可無並沒有強制規定要存在,所以目前我就沒有多去了解了。

再來就是有個key word要注意了 __declspec() 這個可接受的參數有很多,但是目前我僅用到兩個
  1. dllexport
  2. dllimport
假設今天我寫了一個這樣的函式我想要讓別人用dll可以call它,就要這樣寫
extern "C" __declspec(dllexport) int multipleByTwo(int num=2)
{
    std::cout<<"num will be multipled by 2"<<std::endl;
    return num*2;
}
這樣一來被編譯成dll後,在另外一個檔案要用到這函式呢,就要如下面這樣寫,先做個宣告
extern "C" __declspec(dllimport) int multipleByTwo(int num);
宣告之後呢~嘿嘿還記得前面提到的兩種方式嗎?這個方法是implicity way,因為呢,這樣宣告後必須還要link library檔才有辦法編譯成功,要不然就會跑出錯誤提示找不到該函式的reference,另外dll也必須在執行檔的同個目錄喔! 要不然在run time的時候就會錯誤了。另外像這種宣告都會把它放在header file就是了。

至於 explicity way呢,就是用到了LoadLibrary("xxx.dll")這種函式,明顯的去load dll檔,這種方法就不需要像前面那樣link library和宣告 __declspec(dllimport),只是你相對就要去寫一些LoadLibrary和GetProcAddress之類的東西,不過這種的方式我就沒去嘗試了。

不過因為目前我只是測試如何產生dll檔,所以就沒有特別去講究結構了 :) 之後就是來繼續做深入一點的研究,有心得後再上來PO