2015年12月25日 星期五

在樹莓派使用 python gtk2 cairo 製照動畫

安裝
apt-get install python-cairo python-cairo-dev python-gi-cairo python-gtk2 python-gtk2-dev 

把之前寫的程式稍微改一下,就能在樹莓派製作game,gtk3有bug,動畫會停住,還是gtk2好用,本來想寫很多,人一懶工作又累,假日又要運動,每次都寫一些就停掉了 ,之前python寫的程式game,拿到樹莓派,哈哈,還滿順的,有空再據續寫.





read more...

2015年12月18日 星期五

raspberry pi2裝debian8

終於在raspberry pi2裝好debian8,不是樹梅派官方下載的那個jessie,覺的還是debian好用,執行上比其他版本快很多,這個dd完只有幾百M,一開始只能進到文字模式,沒聲音也沒x windows,套件要自己從網路安裝(apt-get install),以前剛玩debiab都是要自己一個個慢慢裝,現在網路快多了,都有整個包好的OS DVD可下載,已經很久沒一個一個裝了,遇到問題google一下應該都有答案.

VLC和KODI很難搞,哈哈,查了網路一堆資料,試了好幾個星期,終於搞定,有點成就感,debian8裝完VCL沒畫面,試很久都失敗,連原碼下載編譯都一樣,換樹梅派官方的那個jessie源裝也失敗,後來大概知道是video output沒裝上,網路上找不到裝video output資料,死馬當活馬醫,apt-cache search把還疑的試著裝看看,竟然有畫面了,不過不順暢,試太多東西可能衝突,就dd一個新的os,重裝VLC,果然就OK,4顆cpu大約60~70%,很順,GPU硬體加速有起來,超興奮,不過只能全螢幕播放.

KODI也是試很久,最後也給我弄起來了,不過只能切換到root,啟動KODI,一般使用者起不來,應該只是權限關係,算了已經很OK了,把樹梅派弄跟桌機差不多一模一樣了.


桌面登入安裝lightdm,桌面管理安裝icewm,聲音系統安裝alsa


收集安裝了一些套件,程式開發anjuta(加glade),mp3音樂播放器xmms2,影音播放器VLC,視聽中心KODI,安裝GPIO,安裝中文輸入和檔案瀏覽器,web瀏覽器,文字編輯器,相片瀏覽器,設定spi-dev model


VLC
KODI



  我的樹莓派  debian8 桌面
新 kernel
xmms2
anjuta
vlc
kodi







read more...

2015年11月20日 星期五

樹梅派

最近開始玩樹梅派,滿有趣的,從露天買了2片UK製樹梅派2開發板,一個7吋官方touch screen,重慶南路天瓏書局也有在賣,也有賣零件感應器,也跑去電子材料行買了一堆電阻和LED,準備好好玩樹梅派,我對電子硬體是白痴,不過不用擔心,網路上有很多教學,或者買些樹梅派的書.


  買的官方7吋touch screen螢幕,裝好畫面上下顛倒,在網路找到方法config.txt加入
    lcd_roate=2

  父母有小孩的,樹梅派對小孩子很有用,可學程式又可認識簡單電子硬體 ,我目前只會接LED,原理很簡單,1端接地,另一端串電阻到GPIO腳位,讓GPIO腳位HIGH(3.3V)或LOW(0V),然後用程式去控制HIGH或LOW,很多程式都能使用GPIO(shell,java,c,python...),官方網站映像安裝,python就能直接使用GPIO了,剛好上次學了python,GPIO用python對我而言,沒甚難的,網路上GPIO LED教學都只是開開關關,我是用thread去點亮LED,讓每個LED都有自己的生命,各自亂閃,很有趣.(原碼下載)








  編輯python和UI部份的IDE,我喜歡用 Anjuta超好用,本來樹梅派OS是裝debian版本,因為Anjuta裝不上,就改用最新ubuntu mate,不過Anjuta有些bug,不能直接樹入中文,只能用貼的.

連接電視畫面




看到網路上有很多高手DIY樹梅派的作品(手機,氣象站,遙控攝影車...),真的很佩服,哈哈

read more...

2015年3月9日 星期一

pygtk cairo動畫(續),使用元件類別,讓各元件做出獨特動作


原碼下載:




在遊戲中飛碟王是用sub_picture.py模組來建立,讓各部元件有自己影像和移動方式,生命低於200,會伸出手臂,攻擊玩家,有教學文件,也有範例


move_object還提使用sub_picture.py模組來建立,讓各部元件有自己影像和移動方式,A和B其實用C都可取代掉的

  1.先import sub_image_fly如下
    from sub_fly_king import sub_image_fly
  2.建立主影像list,這裡只用一元素,可幾個都沒關係,在用move_object建立移動物這樣就OK,0,0是相對座標,如下:
    p=[sub_image("src/png/f1.png",0,0)]
    move_fly_king=move_object(self,self.darea,p,None,None,"RECEVER_collision_fly","SEND_move_fly",self.king2_life)#生命50
  3.隨時可用add_sub_image()來加入sub_image,如下:
                p1=[sub_image_fly1("src/png/fp.png",80,10),sub_image_fly1("src/png/fp.png",20,10),sub_image_fly1("src/png/fp.png",140,10)]#旋轉燈
                p2=[sub_image_fly("src/png/f2.png",-10,0,move_fly_king),sub_image_fly("src/png/f2.png",170,0,move_fly_king)]#攻擊手臂
                move_fly_king.add_sub_image(p1)#加入旋轉燈
                move_fly_king.add_sub_image(p2)#加入攻擊手臂
                move_fly_king.set_move_type("直線X")   
                move_fly_king.set_y(20)
  4.預設sub_image移動是跟move_object是一致,只是多相對座標,預設方法move_update,給主移動物x,y,又傳回去,沒改變甚,如下:
        def move_update(self,x,y):#想成移動物的x,y就可,要如何更新
        return x,y
         
    def get_surface_cr(self,cr,x,y):#給參考點回傳一個cr surface,會被move_object執行續一直呼叫
        self.x,self.y=self.move_update(x,y)[0]+self.x_r,self.move_update(x,y)[1]+self.y_r
        return cr.set_source_surface(self.p,self.x,self.y)
  5.假設我們要讓各部元件有自己移動方式,只要繼承sub_image,然後覆寫move_update這樣就OK,舉個例子上面第3點中sub_image_fly1("src/png/fp.png",80,10),
    就是繼承sub_image如下, 下面move_update,給主移動物x,y,然後每次角度多1度,造成這個部件,繞著主移動物的相對座標(80,10),以半徑10繞圈圈

     from sub_picture import sub_image
     import math,cairo
     class sub_image_fly1(sub_image):
         def __init__(self,image_path,x,y):
             #呼叫了父類別建構式
        super(sub_image_fly1,self).__init__(image_path,x,y)
        self.scale=0
         #super(sub_image_fly,self).__init__(image_path,x,y)
     #複寫父類別move_update
         def move_update(self,x,y):#想成移動物的x,y就可,要如何更新

            self.scale=self.scale+1
            if(self.scale>=360):self.scale=0
            offset_value=(float(self.scale))/180.0*math.pi
            x=x+10*math.cos(float(offset_value))
            y=y+10*math.sin(float(offset_value))
            return x,y

read more...

2015年3月1日 星期日

pygtk cairo動畫(續),增加播放wav聲音檔,播放圖片list,修改鍵盤事件........

原碼下載






我用自己寫的move_object.py動畫引擎模組,很簡單的就寫出了滿複雜的射擊遊戲,當然move_object.py可作很多的動畫,有時間會在嘗試其他遊戲,並增加move_object.py的功能
move_object.py新增了播放圖片list,和預設動作,背景上寫更多文字,圖片list移動物可顯示life,在遊戲中,你會看到大隻飛碟的燈號會閃來閃去,飛碟會向下或朝玩家丟子彈

並增加播放wav聲音檔模組soundwave.py,只是用os.popen,來執行LINUX指令paplay播放wav,目前LINUX音效都是pulseaudio

os.popen('paplay '+self.file)

讓機台很順的左右移動
主程式增加鬆開鍵盤事件connect,鬆開右或左鍵,停止移動
改寫鍵盤壓下事件,壓下左鍵時物件自動一直向,壓下右鍵時物件自動一直向右
............
self.window.connect("key-release-event", self.on_key_up)#鬆開

..............................
move_list_signal.py增加鬆開鍵盤事件


    #鍵盤事件,鬆開
    def on_key_up(self, event,list):
        key = event.keyval
        if key == gtk.keysyms.Right:self.main.move_stop()
        if key == gtk.keysyms.Left:self.main.move_stop()

之前移動物,只單張圖片,現在可用圖片list,會輪流播放圖片,下面是畫出一隻特大飛碟,飛碟燈會閃來閃去

              p=["fly1.png","fly2.png","fly3.png"]
              move_fly_king=move_object.move_object(self,self.framme1,self.darea,p,None,None,"RECEVER_collision_fly","SEND_move_fly",50)#生命50
              move_fly_king.set_move_type("直線X")
              move_fly_king.set_y(70)
              move_fly_king.set_life(100)#設生命
              self.move_list_fly_king.append(move_fly_king)

背景上可用set_text_list,寫更多文字,如下
         list1=["分數:"+str(self.score),self.get_frame_w_h()[0]-180,25,30,"紅色"]
         list2=["射擊遊戲V1.0",12,18,20,"黃色"]
         self.move_obj_b.set_text_list([list1,list2])

預設移動方式,增加2種set_move_foward(x,y),朝x,y方向前進

move_ufo_bullet.set_move_foward(self.move_body.get_xy()[0],self.move_body.get_xy()[1])#朝

角度直線可帶旋轉,第一參數angle=-90直線向上,第2參數5是移動量,若0原地不動,第3參數旋轉半徑,第4參數是旋轉半徑增幅

move_bullet.set_move_angle(angle,5,20,0)

若要讓移動物,繞半徑50,原地旋轉set_move_angle(90,0,50,0)
若要讓移動物,直線向上set_move_angle(-90,5)
.....................

read more...

2015年2月23日 星期一

pygtk cairo動畫(續) 修正 一些BUG,執行效果變好了,穩定版原碼下載

 穩定版原碼下載
用我寫的move_object.py 這模組,已可做出一些想要的動畫,目前我很滿意這模組,修正一些bug,不合程式邏輯(try全拿掉了),並增加一些爆炸畫面抖動效果,哈哈,寫這模組,是純好玩的,和專業的pygame比,當然差很多,不過我這,不用裝其他套件,就可在liux gnome桌面下執行

再來在說一下cairo,我完全弄懂cairo的運作原理了,當初開始寫這模組,清除畫面是用一個透明40x40像素,就算del掉,這surface是不會消失的,時間久,畫圖區充滿了透明40x40像素,queue_draw()就會更新一次,畫面會教lag,我試著用self.p_boom=self.p1=cairo.ImageSurface(cairo.FORMAT_ARGB32,0,0)#無像素png,清除畫面,改善很多,後來我再試著用self.cr=None也可以清除畫面,這更好讓memory也連帶淨空,終於是我要的效果了.
我認為cairo的原理真的很棒,程式稍用亂數改一下座標位置,就讓碰撞物或爆炸畫面產生抖動效果,如下
             offset= random.randint(-self.move_offset,self.move_offset)
             self.cr.set_source_surface(self.p1, self.x+offset, self.y+offset)
真的很棒,實際可看 一下move_object.py原碼,其實程式很短,大部分都在寫一些預設的移動,都只是在處理座標位置self.x,self.y,座標變,queue_draw()更新畫布,物體就會移動,很簡單的,新的畫面


read more...

2015年2月22日 星期日

pygtk cairo動畫(續)執行續,不同物件中信號傳遞

這是一個用move_object.py就完成的,一個不起眼的小遊戲,當然有興趣改一改,可做成其他遊戲,按這下載原碼
執行速度尚可,再來是想讓move_object.py這模組執行更順,才會具續下一步,寫這只是純好玩的,有興趣可拿來修改,參考都很歡迎




move_object.py,用選擇性參數,初始化物件,x=None,y=None,_RECEVER_COLLISION_=None,_RECEVER_MOVE_=None,這4參數可給,可不給,跟java多重建構子功能一樣
,最後_RECEVER_COLLISION_=None,_RECEVER_MOVE_=None,這2參數採用不具方式,用來設定方法,讓使用者在主程式實做,前面是碰撞發送,後者是移動發送,若2者都不給,就是非碰撞物,不做信號實做
    #建構式,用來初始化物件
    def __init__(self,main_obj,frame,darea,image_path,x=None,y=None,_RECEVER_COLLISION_=None,_RECEVER_MOVE_=None):

來看這2參數實際運作,下面getattr(self.main_obj,self.RECEVER_MOVE)(self),呼叫主程式self.main_obj的不具名方法,把本身當參數(self)
主程式,產生move_object物件時把"SEND_move_bullet"當參數,因此_RECEVER_MOVE_就是SEND_move_bullet,此時move_object.py不具名方法就是SEND_move_bullet,就變成self.main_obj.SEND_move_bullet(self),所以主程式就必須實做SEND_move_bullet這個方法(如下面狀況)
主程式
...............................
move_bullet=move_object.move_object(self,self.framme1,self.darea,p,x,y,None,"SEND_move_bullet")

...................
def SEND_move_bullet(self,move_object):
...........................

move_object.py程式


##############################碰撞時透過thread發送信號,把本身和碰撞物當參數,否則送出本身##        
    def SEND_MOVE_SIGNAL(self):
        d="RECEVER"
        if(self.check_yes):
            if(self.is_check_move):#判斷是否要實作移動發送
             #self.main_obj.RECEVER_MOVE(self)
             getattr(self.main_obj,self.RECEVER_MOVE)(self)

###############和其他是否碰撞,若碰撞回傳True,否則False###################################
    def check_collision(self,other_obj):
                  ...................................

          if(self.check_collision_p(xo,yo,xo1,yo1,xs,ys,xs1,ys1)):
              if(self.is_collision_check):
               #在這實作碰撞發送,若碰撞,送出本身和被撞物
               getattr(self.main_obj,self.RECEVER_COLLISION)(self,other_obj)
                           .............................

本遊戲中機台,子彈,飛碟,是碰撞物,因此要實做碰撞或移動發送,也可2者皆做,這裡我才用機台,和子彈是作移動發送,飛碟是作碰撞發送,如下,只要使用move_object這模組,就可完成不同動畫了


    #畫出機台
    def draw_body(self):
          if(len(self.move_list_body)<1 br="">            p="body.png"
            w,h=self.get_frame_w_h()
            #最後2個參數不給,給None代表不作碰撞接收,但作移動發送SEND_move_body,0,0是要產生的位置,也可用set_move_to_xy放到想要位置
            self.move_body=move_object.move_object(self,self.framme1,self.darea,p,w/2-20,h-60,None,"SEND_move_body")
            self.move_list_body.append(self.move_body)
    #畫出子彈
    def draw_bullet(self):        #if key == gtk.keysyms.Right:self.generate()
     if(len(self.move_list_bullet)<6 and="" len="" self.move_list_body="">0):
          p="bullet.png"
          #最後2個參數實作接收移動發送,收移動發送後碰撞檢查
          w,h=self.move_body.get_width_height()[0]/2,self.move_body.get_width_height()[1]
          x,y=self.move_body.get_xy()[0]+w,self.move_body.get_xy()[1]-h
          move_bullet=move_object.move_object(self,self.framme1,self.darea,p,x,y,None,"SEND_move_bullet")
          move_bullet.set_move_type("直線向上")
          move_bullet.set_move_speed(0.005)
          move_bullet.set_explosion_png("boom2.png")
          self.move_list_bullet.append(move_bullet)
          #加到列表list      
  


    #畫出隨機飛碟
    def generate(self):
          p="logo"+str(random.randint(1,5))+".png"
          #最後2個參數實作接收移動發送後碰撞檢查RECEVER_collision和None不作移動發送,代表check碰撞,不給座標None,None,位置會隨機產生
          move_fly=move_object.move_object(self,self.framme1,self.darea,p,None,None,"RECEVER_collision_fly",None)
          speed=float(random.randint(1,100))/100
          move_fly.set_move_speed(speed)
          if(random.randint(1,100) >50 ):
              move_fly.set_move_type("亂數")
          else:
              move_fly.set_move_type("直線X")
          #加到列表list
          self.move_list_fly.append(move_fly)


然後下面實做SEND_move_body,SEND_move_body移動發送,RECEVER_collision_fly碰撞接收,機台單純發送移動,送給move_list_signal做逐一送給飛碟做碰撞檢查,子彈打到最上方就從list移走並爆炸,否則送給move_list_signal做逐一送給飛碟做碰撞檢查,當飛碟檢查到碰撞發生時,RECEVER_collision_fly會收到信號,做碰撞後的後續處理,讓它爆炸,從list移走,增加分數,等等後續處理


##########################################移動發送,碰撞接收實作######################################################
    #接收移動動畫信號,移動物移動時會收到信號,送到move_list_signal作檢查,參數為移動物
    def SEND_move_body(self,move_object):

        #接到移動物,送過來信號,送給move_list_signal逐一檢查碰撞
      
        self.move_list_signal.check_collision(move_object,self.move_list_fly)
    #接收移動動畫信號,移動物移動時會收到信號,送到move_list_signal作檢查,參數為移動物
    def SEND_move_bullet(self,move_object):
        #print(move_object.get_xy()[1])
        #接到移動物,送過來信號,超過視窗上方就移走bullet,否則,送給move_list_signal逐一檢查碰撞
        if(move_object.get_xy()[1]<0-move_object .get_xy="" br="">            #True打到最上面會爆炸,設False到最上面不會爆炸
            if(move_object in self.move_list_bullet):self.move_list_signal.kill_move_obj(move_object,True,self.move_list_bullet)
        else:          
            self.move_list_signal.check_collision(move_object,self.move_list_fly)
    #接收移動物和碰撞物,碰撞時會收到信號,參數為移動物和碰撞物送給move_list_signal移走移動物,和碰撞物
    #飛碟是作碰撞,第一個參數傳本身,第2個參數傳實作移動,發送的是機台或子彈
    def RECEVER_collision_fly(self,c_object,move_object):
        #print(c_object.get_name())
        #更改了爆炸圖片,大隻飛碟爆炸不同
        if(c_object.get_name()=="logo4.png"):c_object.set_explosion_png("boom1.png")
      
        #self.move_body=None
        #從list中移走移動物,和碰撞物,True代表產生爆炸畫面,若不要爆炸畫面設False,注意是那個list移走,否則不會爆炸
        self.move_list_signal.kill_move_obj(c_object,True,self.move_list_fly)
        if(move_object in self.move_list_body):
            self.move_list_signal.kill_move_obj(move_object,True,self.move_list_body)
            self.GAME_OVER()
            self.score=0
        if(move_object in self.move_list_bullet):
            #設子彈爆炸不一樣,畫面較逼真
            #move_object.set_explosion_png("boom2.png")
            self.move_list_signal.kill_move_obj(move_object,True,self.move_list_bullet)
            self.score=self.score+1
            self.show_score()
            if(self.score>self.score_high):self.score_high=self.score#不要在執行續中呼叫
read more...

2015年2月19日 星期四

pygtk Cairo動畫(續)碰撞信號處理實做

程式原碼下載



我寫的move_object.py這模組,算蠻強大的,後續會應用這模組,製造更多動畫,主程式下面以move_object.py我製造一張會抖動的太空背景,因ubuntu1410,我先關掉抖動,後續在改,若是debian可把
          #self.move_obj_b.set_move_speed(.5)
          #self.move_obj_b.set_move_type("背景")
打開,就會有抖動效果,按方向建右鍵增加移動飛碟,若飛碟相撞會產生爆炸,並從視窗消失,按方向建左鍵移走飛碟

    #畫出背景
    def draw_background(self):
          p="background.png"
          #ubuntu1410 bug座標不能0,0
          self.move_obj_b=move_object.move_object(self,self.framme1,self.darea,p,1,1)
          #self.move_obj_b.set_move_to_xy(0,0) ubuntu1410 don't work跑到0,0會X error
          #若不要背景移動,下面不要設定
          #self.move_obj_b.set_move_speed(.5)
          #self.move_obj_b.set_move_type("背景")

    #畫出隨機飛碟
    def generate(self):
          p="logo"+str(random.randint(1,3))+".png"
          move_obj=move_object.move_object(self,self.framme1,self.darea,p)
          speed=float(random.randint(1,100))/100
          move_obj.set_move_speed(speed)
          if(random.randint(1,100) >50 ):
              move_obj.set_move_type("亂數")
          else:
              move_obj.set_move_type("直線X")
          #加到列表list
          self.move_list_signal.move_list_append(move_obj)


再來重要的動畫碰撞信號實做,來看看如何實做,到目前程式很少就做出了很複雜動作,其實在於邏輯是否夠好,可行,程式就會簡潔有力,在move_object.py修改了SEND_MOVE_SIGNAL方法,碰撞時呼叫self.main_obj.RECEVER否則呼叫self.main_obj.RECEVER_MOVE

    #碰撞時透過thread發送信號,把本身和碰撞物當參數,否則送出本身       
    def SEND_MOVE_SIGNAL(self):
        if(self.is_check_collision):
            self.main_obj.RECEVER(self,self.c_obj)
        else:
            self.main_obj.RECEVER_MOVE(self)


在move_object.py增加了碰撞方法,碰撞產生時self.is_check_collision=True,此時SEND_MOVE_SIGNAL改成呼叫self.main_obj.RECEVER,一開始self.is_check_collision=False

    def check_collision(self,other_obj):

         

          xo,yo=other_obj.get_xy()
          xo_w,yo_h=other_obj.get_width_height()
          xo1=xo+xo_w
          yo1=yo+yo_h
          #本身座標和寬高
          xs,ys=self.get_xy()
          xs_w,ys_h=self.get_width_height()
          xs1=xs+xs_w
          ys1=ys+ys_h
          #print(xo,yo,xo1,yo1,xs,ys,xs1,ys1)
          if(self.is_collision(xo,yo,xo1,yo1,xs,ys,xs1,ys1)):
              self.is_check_collision=True
              self.c_obj=other_obj
              self.set_move_type=None#碰撞後不要在移動了

在來看主程式,如何接收信號,非碰撞時接收RECEVER_MOVE,然後將移動物參數送給move_list_signal(類似界面),move_list_signal這個類,會將移動物送給各個移動物,做碰撞判斷,若主程式收到碰撞信號RECEVER,會透過move_list_signal這個類,會將移動物和被碰撞物刪除,並產生爆炸畫面,簡單邏輯,就能造成強大效果,做出複雜的動作,很神奇吧!


    #接收移動動畫信號,移動物移動時會收到信號,送到move_list_signal作檢查,參數為移動物
    def RECEVER_MOVE(self,move_object):

        #檢查碰撞,若不要檢查,忽略
        self.move_list_signal.check_collision(move_object)

    #接收移動動畫信號,移動物,和碰撞物碰撞時會收到信號,參數為移動物和碰撞物
    def RECEVER(self,move_object,c_object):
        #print(move_object.get_name())
        #從list中移走移動物,和碰撞物,True代表產生爆炸畫面,若不要爆炸畫面設False
        self.move_list_signal.kill_move_obj(move_object,True)
        self.move_list_signal.kill_move_obj(c_object,True)
  其餘看程式註解,下面是執行畫面


read more...

2015年2月18日 星期三

pygtk cairo動畫(續) BUG修正(debian 7.7 memory增加的問題),增加鍵盤事件

程式原碼下載


修正在debian7.7下,memory一直增加問題,以及所有OS,執行會漰潰的問題.


memory增加是deb7.7 pygtk3.0的問題,改用gtk2.0正常

執行會漰潰的問題是gtk button元件的問題(使用者一直按button可能crash),改用鍵盤正常

避開BUG,就可好好來使用pygtk cairo製作想要的動畫


鍵盤事件,如下,把key-press-event事件跟 self.window連結,指定處理方法on_key_down
這裡實做是放到move_list_singal模組中,當使用者按下方向鍵右鍵,增加一隻移動物,每支
移動物都有自己特徵,按下方向鍵左鍵,刪除一隻移動物,移動物速度都不一樣,移動方式目前有2種,亂移,跟水平亂移,要怎移動,只是在加上去


main.py
...................................................................................................................
        self.window.connect("key-press-event", self.on_key_down)
....................................................................................................................

    #處理鍵盤,由move_list_signal物件來實作,呼叫self.move_list_signal.on_key_down(event)
    def on_key_down(self, widget, event):
        key = event.keyval
        self.move_list_signal.on_key_down(event)
        #if key == gtk.keysyms.Right:self.generate()




move_list_signal.py
.................................
    #鍵盤事件,若想不同動作,可繼承複寫
    def on_key_down(self, event):
        key = event.keyval
        if key == gtk.keysyms.Right:
            self.main.generate()
        if key == gtk.keysyms.Left:
            self.kill_move_first()




要增加移動方式 ,self.move_type是設定type給begin_move方法來判斷,程式碼,都有詳細註解


    #移動方式
    def set_move_type(self, mtype):
        self.move_type=mtype


    def begin_move(self,):#不給type為靜止
        if(self.move_type=="亂數"):self.set_move_random()
        if(self.move_type=="直線X"):self.set_move_line_x_radom()
   

執行畫面



read more...

2015年2月15日 星期日

pygtk cairo 如何讓每一個動畫都有自己行為,以及如和建立每一個動畫信號

move_object.py以做修正,它不應該含有gtk ui部份,我希望它可以在畫圖區可以一直產生,若含有ui這樣是不對的,當然這邏輯很棒,每一隻都是一個執行續,擁有自己的生命行為,
我到可讓隻飛碟行徑路線,生命都不一樣,一般人可能只會想到用一個thread讓每隻動起來,這樣邏輯不好,而且程式複雜化了.
目前,每按一次按鈕,就會在frame隨機位置,隨機產生3種飛碟中之一種,每隻飛碟速度都不同,按停止扭,會一隻一隻的清空,我是每隻一一,用空白圖來清空
move_list_signal.py用來存放,每隻飛碟的list,到時每隻飛碟信號應該會這模組建立,有點像界面,做溝通用的,

說真的,這樣的邏輯思考,已經算滿神奇的,在來看一下,更神奇的信號處理,我如何讓飛碟飛碟一直增加,又能每隻飛碟能讓信號傳回主程式
  
下面START方法,按產生飛碟,就會建立一個move_object物件(就是一台亂跑的飛碟),用self把本身整個傳給每一隻建立的move_object物件,那move_object接收
這參數,就可使用RECEVER發法,又把自己self回傳給主程式,主程式可接收move_object所有東西



        #接收移動動畫信號,print印出每隻檔案名(測試用沒什意義,到時還要完成)
    def RECEVER(self,move_object):
        print(move_object.get_name())


    def START(self, widget, *event):
        if bt_active_label=='產生飛碟':
                  ...............................................................
          move_obj=move_object.move_object(self,self.framme1,self.darea,p)
                  ...............................................................


在來看move_object如何送信號過來,self.main_obj就是接收了主程式self,SEND_MOVE_SIGNAL方法呼叫了主程式中的RECEVER,然後move_object自帶的thread
會一直呼叫self.move_object.SEND_MOVE_SIGNAL(),終端機就看到每隻飛碟的3種飛碟檔名一直被印出來了,很神奇吧!!這樣就可用來做碰撞判斷用,其實我還試著想有沒有什方法
直接就可把飛碟信號傳給每隻飛碟,這樣的或應該就是超神的了,只想到透過類似界面,這界面會存放所有飛碟,主程式接受到每隻飛碟傳過來信號,逐一用for來傳這,
就是我在建立move_list_signal.py的原因,執行畫畫面就像下圖一樣,然後這是目前的程式源碼下載,有需要可拿去參考,只是無聊愛亂玩程式,哈哈哈
class move_object():
    #建構式,用來初始化物件
    def __init__(self,main_obj,frame,darea,image_path):
        self.main_obj=main_obj
                ..........................................................
         #初始化thread,把自己當參數,傳給thread,用來製照動畫,並啟動它
        self.thread = MyThread(self)       ...........................................
    def SEND_MOVE_SIGNAL(self):
        self.main_obj.RECEVER(self)

class MyThread(Thread):
    def __init__(self,move_obj):
        super(MyThread,self).__init__()
        self.move_object=move_obj
        ..........................
      

    def run(self):#執行續,執行的地方
            print("執行續啟動")
            while self.is_move:
                           ......................................
                   self.move_object.SEND_MOVE_SIGNAL()
                                   .....................................


read more...

2015年2月13日 星期五

pygtk3.0 cairo動畫

又更進一步了,製造動畫,會選cairo,是想自己寫,當然其他有更複雜好用的module譬如pygame,pygame我不懂,cairo也是剛開始學,看了網路上的例子,終於稿懂了,下面是一個會在視窗隨機移動的圖片,因為只完成一點點,可能會跑出視窗外,重點是cairo讓圖片動起來,當然一定要用執行續的方式,這應該都知道.

現在來說原理,

self.darea = Gtk.DrawingArea()
只是制造一個畫圖區

self.p1 = cairo.ImageSurface.create_from_png("src/logo.png")
 制造一個image cairo圖層

關鍵

1.
self.darea.connect("draw", self.expose)
指畫畫這件事會被觸發 self.expose,pygtk3.0已經改成"draw"

 2.
畫出圖的方式
    def expose(self, widget, event):
         #創造cairo畫布區域
         self.cr = widget.get_property("window").cairo_create()
         #圖層來源
         self.cr.set_source_surface(self.p1, self.x, self.y)
         #畫出
         self.cr.paint()   


3.執行續是只要做
self.darea.queue_draw()動作就可以,畫畫這件事就畫觸發self.expose,畫面就會update到最新狀態,一開始就是不懂這原理,一直用執行續跑expose(),結果圖片是一直加上去,在網路上看很多例子,就想通了

來看下面的例子


# move_object.py
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
#
# Copyright (C) 2015 -
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#預計目的,給個window參數,圖片參數,產生一個動畫,具有各種移動方式,速度,給個碰撞機制,爆炸畫面,生命結束
import os, sys
import time, cairo,random
from threading import Timer,Thread,Event
from gi.repository import GObject
from gi.repository import Gtk, Gdk
class move_object(Gtk.DrawingArea):
    #建構式,用來初始化物件
    def __init__(self,window):
        self.window=window
        self.darea = Gtk.DrawingArea()
        #製照image cairo圖層
        self.p1 = cairo.ImageSurface.create_from_png("src/logo.png")
        #self.darea.set_size_request(250, 150)
        #self.cr = self.window.get_property("window").cairo_create()
        #self.cr = cairo.Context(self.p1)
        #畫布事件expose方法
        self.darea.connect("draw", self.expose)
        self.window.add(self.darea)
        #self.window.queue_draw_area(0,0,300,300)
        self.move_type=""#動畫移動方式
        self.speed=0.1#動畫速度預設更新為0.1second
        #初始位置
        self.x=random.randint(0,self.window.get_allocation().width)
        self.y=random.randint(0,self.window.get_allocation().height)
        #self.x=0
        #self.y=0
        #初始化thread,把自己當參數,傳給thread,用來製照動畫,並啟動它
        self.thread = MyThread(self )
        self.thread.start()
    #畫出圖
    def expose(self, widget, event):
         #創造cairo畫布區域
         self.cr = widget.get_property("window").cairo_create()
         #圖層來源
         self.cr.set_source_surface(self.p1, self.x, self.y)
         #畫出
         self.cr.paint()       


    #更新畫布,已經有畫布,執行續只要queue_draw()既可,會觸發expose,不是用執行續去執行expose
    def re_draw(self):
        self.darea.queue_draw()
    #更新速度
    def set_move_speed(self,speed):
        self.speed=speed
    #取得速度
    def get_move_speed(self):
        return self.speed
   
    #移動位置,更新圖片位置   
    def set_move_to_xy(self, x, y):
         self.x=x
         self.y=y
    def get_xy(self, x, y):
         return self.x,self.y

    #移動方式
    def set_move_type(self, mtype):
        self.move_type=mtype

    def set_move_random(self):
        self.x = self.x+random.randint(-1, 1)*5
        self.y = self.y+random.randint(-1, 1)*5
    def kill_move(self):
        self.thread.stop()
   

#此行一定要加,否則執行續,會停住
GObject.threads_init()   
#執行續,不能restart,只能重新產生,並等run執行完,會自動KILL

class MyThread(Thread,Gtk.DrawingArea):
    def __init__(self,move_obj):
        super(MyThread,self).__init__()
        self.move_object=move_obj
        self.stopped = Event()
        self.is_move=True
       

    def run(self):#執行續,執行的地方
            print("執行續啟動")
            while self.is_move:
             #因為thread一起動,便不能中止,指能讓run不做任何事,走完自動KILL
                   #self.move_object.set_move_to_xy(x*2,x*2)
                   #set_move_random()變更圖片位置
                   self.move_object.set_move_random()
                   #更新畫布
                   self.move_object.re_draw()
                   time.sleep(self.move_object.get_move_speed())
            print("執行續結束")
        #except:pass
    #讓執行續,快速結束
    def stop(self):
        self.stopped.set()
        self.is_move=False

read more...

2015年2月8日 星期日

PYTHON的繼承

 圖片播放器原碼下載

 新增了一個播放效果
執行樣本:







試了使用Python的繼承方式,實際操作一次,大概就了解如何運作Python的繼承,這裡我沒用到覆寫,所謂覆寫,就是子類別,重寫父類別Method,就是def名稱一樣

我又新增了tree_show模組(tree_show.py),繼承了treeview_filechooser模組,首先treeview_filechooser必須是一個object,而非class,如下的A

A.t_filechosser是一個object,繼承了object

  class t_filechosser(object):

B.t_filechosser是一個class

  class t_filechosser():


為甚物件導向要使用繼承,就是要程式碼簡單,重複使用,不需要重寫程式碼,看一下tree_show是如何繼承t_filechosser,小括號內就是被繼承對象
tree_show建構式比t_filechosser多了image,view初始化參數,是處理圖片要用到的,用super呼叫了父類別建構式,以便能夠初始化父類別,
在tree_show只出始化self.image_viewport_new,self.image,self.pixbuf(如下面程式碼),其他都是在父類別初始化的

邏輯和構想,目的我希望t_filechosser專注在樹狀表單(檔案選擇器),到時要寫其他就不用改甚,可用在其他如播放MP3的表單等
,讓tree_show則專注在處理圖片方面,讓程式簡單易讀,程式碼重複使用率提高


class tree_show(treeview_filechooser.t_filechosser):
    def __init__(self,add_widget,window,_type_,ifile,image,view):
        #呼叫了父類別建構式
        super(tree_show,self).__init__(add_widget,window,_type_,ifile)
        self.tt.connect('cursor-changed', self.SHOW_ITEM_SIGNAL)
        #self.tt.connect('cursor-changed', self.SHOW_ITEM_MOVE)
        self.image_viewport_new=view
        self.image=image
        self.pixbuf = Pixbuf.new_from_file(self.file)

在tree_show中的4個方法,實做了處理圖片方法(須透過配合size_image類別)

def SHOW_ITEM_MOVE(self):取得2張圖片,丟給size_image類別,造成移動效果,resize效果
def SHOW_ITEM(self):取得1張圖片,丟給size_image類別,造成resize效果
def call_resize(self):非執行續啟動時resize效果
def SHOW_ITEM_SIGNAL(self, widget,*event):非執行續啟動時,點選列表造成resize效果

來看主程式如何使用,首先產生了tree_show物件self.t_filechosser,如下初始化tree_show,同始t_filechosser也會被初始化,雖然沒產生t_filechosser物件,
但t_filechosser中的方法,都可在tree_show物件使用(如下),這就是繼承的好處

..................
#初始化,tree_show,tree_show是繼承t_filechosser,在t_filechosser的方法(def),都可使用
self.t_filechosser=tree_show.tree_show(self.scrolledwindow1,self.window,"DELETE",self.file,self.image,self.image_viewport_new)
.............................
if bt_active_label=="停止播放":self.t_filechosser.KILL_TIMER()
if bt_active_label=="由右向左播放":self.t_filechosser.SHOW_TIMER(self.time,"TYPE_MOVE")
....................................

特別重要的是不同類別中的溝通,當A類別產生B類別物件,B類別回過頭去即時使用A類別方法,就是個問題,此時是不能用產生A類別物件再去呼叫的,重新初始化記憶體資料就不一樣了
那要如何作到,一般可宣告成static method,可解決這種情況,python不是很熟,試一下沒完成,就沒試了,我改試了下面這個方式

有趣的一點,我發現A類別竟然可用self把自己本身當參數,整個傳給產生的B物件,過程就像下面這樣,真的很有趣,因為好久沒寫程式,之前在玩QT,JAVA,是不是這樣,真的忘了
我寫程式也喜歡try來try去,寫程式只是玩票性質,無聊亂玩

import b
class a():
 def __init__(self)
 selfb=b(self)#產生b物件,並把整個class a當參數傳給b,self參數就是整個class a
 selfb.B()#使用b類別B()方法
 def A(self):
   print("class A")



class b():
 def __init__(self,obj)
  selfa=obj
 def B(self):
   selfa.A()#使用A類別A()方法

下面在treeview_filechooser.py中就有使用到此技巧,class t_filechosser用self參數,整個傳給了執行續


class t_filechosser(object):

........
 def SHOW_TIMER(self,t_time,_type_):
   #thread停止,才能在產生thread    ,判定thread是否停止
   #if(self.thread.is_run_stop()):
   self.t_time=t_time
   if(not self.thread.isAlive()):
       #產生thread,self.play_begin_number是選中項編號,注意最後參數,將自己傳給了thread,使得thread能使用tree_show的method
      self.thread = MyThread(self.tt,self.t_time,self.play_begin_number,_type_,self)
      print('NUMBER',self.play_begin_number)
      #開始thread
      self.thread.start()
........     

下面MyThread中self.tree_obj.SHOW_ITEM_MOVE(),使用了t_filechosser的方法,即時取t_filechosser的資料

class MyThread(Thread):
.........
    def run(self):#執行續,執行的地方
       
        #try:
        #while not self.stopped.wait(self.t):
            #判斷播放完,self.player_number應設成0,在重播
            if(self.player_number+1>=self.len):self.player_number=0
            print("執行續啟動")
            for x in range(self.player_number,self.len):
             #因為thread一起動,便不能中止,指能讓run不做任何事,走完自動KILL
             if(self.is_PLAY):
                  #try:
                   #設定treeview選中項目,因為cursor-changed所以會發出信號,調整圖片,定時選中圖片,
                   #Gdk.threads_enter()
                   #GObject.idle_add(self.tt.set_cursor(x))
                   self.tt.set_cursor(x)
                   #選中列表,根據type,由繼承者實作Method,SHOW_ITEM_MOVE(),SHOW_ITEM()
                   if(self.type=="TYPE_MOVE"):self.tree_obj.SHOW_ITEM_MOVE()
                   if(self.type=="TYPE_STEP"):self.tree_obj.SHOW_ITEM()
                   #Gdk.threads_leave()
                   time.sleep(self.t)
                  #except:self.stop()
             #self.stopped.wait(1)
            print("執行續結束")
        #except:pass
試了使用Python的繼承方式,實際操作一次,大概就了解如何運作Python的繼承,這裡我沒用到覆寫,所謂覆寫,就是子類別,重寫父類別Method,就是def名稱一樣

我又新增了tree_show模組(tree_show.py),繼承了treeview_filechooser模組,首先treeview_filechooser必須是一個object,而非class,如下的A

A.t_filechosser是一個object,繼承了object

  class t_filechosser(object):

B.t_filechosser是一個class

  class t_filechosser():


為甚物件導向要使用繼承,就是要程式碼簡單,重複使用,不需要重寫程式碼,看一下tree_show是如何繼承t_filechosser,小括號內就是被繼承對象
tree_show建構式比t_filechosser多了image,view初始化參數,是處理圖片要用到的,用super呼叫了父類別建構式,以便能夠初始化父類別,
在tree_show只出始化self.image_viewport_new,self.image,self.pixbuf(如下面程式碼),其他都是在父類別初始化的

邏輯和構想,目的我希望t_filechosser專注在樹狀表單(檔案選擇器),到時要寫其他就不用改甚,可用在其他如播放MP3的表單等
,讓tree_show則專注在處理圖片方面,讓程式簡單易讀,程式碼重複使用率提高


class tree_show(treeview_filechooser.t_filechosser):
    def __init__(self,add_widget,window,_type_,ifile,image,view):
        #呼叫了父類別建構式
        super(tree_show,self).__init__(add_widget,window,_type_,ifile)
        self.tt.connect('cursor-changed', self.SHOW_ITEM_SIGNAL)
        #self.tt.connect('cursor-changed', self.SHOW_ITEM_MOVE)
        self.image_viewport_new=view
        self.image=image
        self.pixbuf = Pixbuf.new_from_file(self.file)

在tree_show中的4個方法,實做了處理圖片方法(須透過配合size_image類別)

def SHOW_ITEM_MOVE(self):取得2張圖片,丟給size_image類別,造成移動效果,resize效果
def SHOW_ITEM(self):取得1張圖片,丟給size_image類別,造成resize效果
def call_resize(self):非執行續啟動時resize效果
def SHOW_ITEM_SIGNAL(self, widget,*event):非執行續啟動時,點選列表造成resize效果

來看主程式如何使用,首先產生了tree_show物件self.t_filechosser,如下初始化tree_show,同始t_filechosser也會被初始化,雖然沒產生t_filechosser物件,
但t_filechosser中的方法,都可在tree_show物件使用(如下),這就是繼承的好處

..................
#初始化,tree_show,tree_show是繼承t_filechosser,在t_filechosser的方法(def),都可使用
self.t_filechosser=tree_show.tree_show(self.scrolledwindow1,self.window,"DELETE",self.file,self.image,self.image_viewport_new)
.............................
if bt_active_label=="停止播放":self.t_filechosser.KILL_TIMER()
if bt_active_label=="由右向左播放":self.t_filechosser.SHOW_TIMER(self.time,"TYPE_MOVE")
....................................

特別重要的是不同類別中的溝通,當A類別產生B類別物件,B類別回過頭去即時使用A類別方法,就是個問題,此時是不能用產生A類別物件再去呼叫的,重新初始化記憶體資料就不一樣了
那要如何作到,一般可宣告成static method,可解決這種情況,python不是很熟,試一下沒完成,就沒試了,我改試了下面這個方式

有趣的一點,我發現A類別竟然可用self把自己本身當參數,整個傳給產生的B物件,過程就像下面這樣,真的很有趣,因為好久沒寫程式,之前在玩QT,JAVA,是不是這樣,真的忘了
我寫程式也喜歡try來try去,寫程式只是玩票性質,無聊亂玩

import b
class a():
 def __init__(self)
 self.b=b(self)#產生b物件,並把整個class a當參數傳給b,self參數就是整個class a
 self.b.B()#使用b類別B()方法
 def A(self):
   print("class A")



class b():
 def __init__(self,obj)
  self.a=obj
 def B(self):
   self.a.A()#使用A類別A()方法

下面在treeview_filechooser.py中就有使用到此技巧,class t_filechosser用self參數,整個傳給了執行續


class t_filechosser(object):

........
 def SHOW_TIMER(self,t_time,_type_):
   #thread停止,才能在產生thread    ,判定thread是否停止
   #if(self.thread.is_run_stop()):
   self.t_time=t_time
   if(not self.thread.isAlive()):
       #產生thread,self.play_begin_number是選中項編號,注意最後參數,將自己傳給了thread,使得thread能使用tree_show的method
      self.thread = MyThread(self.tt,self.t_time,self.play_begin_number,_type_,self)
      print('NUMBER',self.play_begin_number)
      #開始thread
      self.thread.start()
........     

下面MyThread中self.tree_obj.SHOW_ITEM_MOVE(),使用了t_filechosser的方法,即時取t_filechosser的資料

class MyThread(Thread):
.........
    def run(self):#執行續,執行的地方
       
        #try:
        #while not self.stopped.wait(self.t):
            #判斷播放完,self.player_number應設成0,在重播
            if(self.player_number+1>=self.len):self.player_number=0
            print("執行續啟動")
            for x in range(self.player_number,self.len):
             #因為thread一起動,便不能中止,指能讓run不做任何事,走完自動KILL
             if(self.is_PLAY):
                  #try:
                   #設定treeview選中項目,因為cursor-changed所以會發出信號,調整圖片,定時選中圖片,
                   #Gdk.threads_enter()
                   #GObject.idle_add(self.tt.set_cursor(x))
                   self.tt.set_cursor(x)
                   #選中列表,根據type,由繼承者實作Method,SHOW_ITEM_MOVE(),SHOW_ITEM()
                   if(self.type=="TYPE_MOVE"):self.tree_obj.SHOW_ITEM_MOVE()
                   if(self.type=="TYPE_STEP"):self.tree_obj.SHOW_ITEM()
                   #Gdk.threads_leave()
                   time.sleep(self.t)
                  #except:self.stop()
             #self.stopped.wait(1)
            print("執行續結束")
        #except:pass









read more...

2015年1月31日 星期六

pygtk圖片2疊到圖片1





google找到了,這裡是gnome文件說明

試了一下,把長頸鹿,疊到獅子,composite可帶透明度,scale則沒有(如下)



P2疊到P3
         Pixbuf.scale(P2,P3,0, 0, 50, 50, 0, 0, 1.0, 1.0, InterpType.BILINEAR)
P2疊到P
     Pixbuf.composite(P2, P1,0, 0, 50, 50, 0, 0, 1.0, 1.0, InterpType.BILINEAR, 127)



# main.py
# Copyright (C) 2015 yplin
#
# PIXBUF_IMAGE is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PIXBUF_IMAGE is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see .

from gi.repository import Gtk, GdkPixbuf, Gdk
import os, sys
from gi.repository.GdkPixbuf import Pixbuf, InterpType

#Comment the first line and uncomment the second before installing
#or making the tarball (alternatively, use project variables)
UI_FILE = "src/pixbuf_image.ui"
#UI_FILE = "/usr/local/share/pixbuf_image/ui/pixbuf_image.ui"


class GUI:
    def __init__(self):

        self.builder = Gtk.Builder()
        self.builder.add_from_file(UI_FILE)
        self.builder.connect_signals(self)
        self.image1=self.builder.get_object('image1')
        self.image2=self.builder.get_object('image2')
        self.image3=self.builder.get_object('image3')

        window = self.builder.get_object('window')
        P1= Pixbuf.new_from_file("src/logo.png")
        P2 = Pixbuf.new_from_file("src/p.jpeg")
        P3= Pixbuf.new_from_file("src/logo.png")
        Pixbuf.scale(P2,P3,0, 0, 50, 50, 0, 0, 1.0, 1.0, InterpType.BILINEAR)
        Pixbuf.composite(P2, P1,0, 0, 50, 50, 0, 0, 1.0, 1.0, InterpType.BILINEAR, 127)
        self.image1.set_from_pixbuf(P1)
        self.image2.set_from_pixbuf(P2)
        self.image3.set_from_pixbuf(P3)
        #Pixbuf.composite(P2, P1,0, 0, 50, 50, 0, 0, 1.0, 1.0, InterpType.BILINEAR, 127)
       


        window.show_all()

    def on_window_destroy(self, window):
        Gtk.main_quit()

def main():
    app = GUI()
    Gtk.main()
       
if __name__ == "__main__":
    sys.exit(main())


read more...

2015年1月29日 星期四

用python執行續來新增了播放圖片(download)

源碼下載(debian7.7,ubuntu1410測試OK,版本接近,或更新應該都OK)

畫面如下


video



我從ubuntu1410換到Debian7.7寫程式了,還是Debian穩定,而且文定多了


我在treeview_filechooser.py模組再新增一個MyThread執行續類別,處理播放圖片,開頭一定要加GObject.threads_init()

#此行一定要加,否則執行續,會停住
GObject.threads_init()    
#執行續,不能restart,只能重新產生,並等run執行完,會自動KILL
class MyThread(Thread):

.......................
.....................

若讓主程式用迴圈處理,其他事就不能做了,迴圈若放到執行續,就能同時進行
好比一個人就只能同時作一件事,多一個人就能再做一件事,執行續就像幫手

1.執行續,一旦啟動,就不能停止,只能等run()跑完,他會自動KILL
2.既然不能停止,只能讓他不做事,設個IF,然後由bollean控制它(如下)
   run()
       for ............:
         if(self.bollean)
           ...............

   def do_run(self,b):#決定是否工作
     self.bollean=b
3.執行續,不可能永遠存在run()跑完,他會自動KILL

  錯誤
  self.thread = MyThread(....)
  self.thread.start()
  ...........................
  #跑完,再啟動
  self.thread.start()

  正確
  self.thread = MyThread(....)
  self.thread.start()
  ...........................
  #跑完,再啟動
  self.thread = MyThread(....)
  self.thread.start()

程式邏輯,當按下播放紐,啟動執行續,讓他取得開始播放位置,並計算列表長度,for逐一選中列表,
列表選中cursor-changed信號被觸發,信號連到調整圖片大小,中途想停止播放,設個IF來判斷(run如下)

    def run(self):#執行續self.thread.start()執行的地方
      
        try:
        #while not self.stopped.wait(self.t):
            #判斷播放完,self.player_number應設成0,在重播
            if(self.player_number+1>=self.len):self.player_number=0
            print("my thread start")
            for x in range(self.player_number,self.len):
             #因為thread一起動,便不能中止,指能讓run不做任何事,走完自動KILL
             if(self.is_PLAY):
                  try:
                   #設定treeview選中項目,因為cursor-changed所以會發出信號,調整圖片
                   #Gdk.threads_enter()
                   #GObject.idle_add(self.tt.set_cursor(x))
                   self.tt.set_cursor(x)
                   #Gdk.threads_leave()
                   time.sleep(self.t)
                  except:self.stop()
             #self.stopped.wait(1)
            print("my thread stop")
        except:pass
    #讓執行續,快速結束
    def stop(self):
        self.stopped.set()
        self.is_PLAY=False
      
    #設定True或False,決定是否resize image
    def set_PLAY(self,boolean):
        self.is_PLAY=boolean
    #設定播放號碼
    def set_play_number(self,i):
        self.player_number=i




def set_PLAY(self,boolean)設定是否設定列表,當使用者按停止self.is_PLAY為False,播放停止
def set_play_number(self,i)當使用者未按播放紐,選中列表,告訴run播放位置
def stop(self)讓執行續,快速結束,跟set_PLAY(False)一樣,多了self.stopped.set(),會更快結束

另外,ui檔用glade多了scale元件,scale放入調整的adjustment1,來控制速度,get_value()可取得調整值


read more...

2015年1月27日 星期二

pygtk圖片檢視器完成了(程式下載)

算是有點水準的圖片檢視器(原碼下載)

下面是在ubuntu1410執行後樣子,OS版本不要太舊,應該都可執行

 








已經找到python file使用中文的方式,開頭給字形
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- 

圖片拉大後可拉小了,要將viewport先加進捲動視窗(如下圖)


多加了檔案列表類別t_filechosser,處理檔選擇器被選取後,把檔案加入TreeView列表中,當TreeView列表被選取時,信號呼叫重新調整image大小後顯示,其實有些可以用Glade來設計,用glade會有些不滿意,乾脆這部份就不用glade,成一個CLASS,日後也可拿來用,譬如音樂播放氣

另外加強image_viewport這類別的功能,能夠保持等比例縮小或放大圖片

程式碼共有4個檔案(一個glade建立的ui檔,3個用Anjuta IDE編輯的py檔)如下

 pygtk_foobar_u1410.ui

 <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.18.3 -->
<interface>
  <requires lib="gtk+" version="3.0"/>
  <object class="GtkFileFilter" id="filefilter1">
    <patterns>
      <pattern>*.jpg</pattern>
      <pattern>*.png</pattern>
      <pattern>*.PNG</pattern>
      <pattern>*.JPG</pattern>
    </patterns>
  </object>
  <object class="GtkListStore" id="liststore1">
    <columns>
      <!-- column-name gchararray1 -->
      <column type="gchararray"/>
    </columns>
  </object>
  <object class="GtkWindow" id="window">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="hexpand">False</property>
    <property name="vexpand">False</property>
    <property name="title" translatable="yes">圖片檢視器</property>
    <property name="window_position">center</property>
    <property name="default_width">700</property>
    <property name="default_height">502</property>
    <property name="gravity">center</property>
    <signal name="check-resize" handler="IMAGE_RESIZE" object="viewport1" swapped="no"/>
    <signal name="destroy" handler="on_window_destroy" swapped="no"/>
    <child>
      <object class="GtkPaned" id="paned1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <child>
          <object class="GtkBox" id="box1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkButton" id="button6">
                <property name="label" translatable="yes">  選    擇    圖    片    檔       ...  </property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="IMAGE_VIEWER" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button3">
                <property name="label" translatable="yes">離開</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW_IMAGE" object="window" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">1</property>
              </packing>
            </child>
            <child>
              <object class="GtkScrolledWindow" id="scrolledwindow1">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="shadow_type">in</property>
                <child>
                  <placeholder/>
                </child>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">2</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button2">
                <property name="label" translatable="yes">隱藏圖片</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_IMAGE" object="image1" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">3</property>
              </packing>
            </child>
            <child>
              <object class="GtkLabel" id="label1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="margin_right">4</property>
                <property name="label" translatable="yes">訊息</property>
                <property name="ellipsize">start</property>
                <property name="max_width_chars">0</property>
                <attributes>
                  <attribute name="font-desc" value="<輸入數值> 18"/>
                  <attribute name="style" value="italic"/>
                  <attribute name="weight" value="bold"/>
                  <attribute name="foreground" value="#cccc00000000"/>
                </attributes>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="padding">20</property>
                <property name="position">4</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button1">
                <property name="label" translatable="yes">顯示圖片</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="yalign">0.44999998807907104</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_IMAGE" object="image1" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">5</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button4">
                <property name="label" translatable="yes">縮放</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_TYPE" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">6</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="button5">
                <property name="label" translatable="yes">擴展</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <signal name="clicked" handler="SHOW" object="label1" swapped="no"/>
                <signal name="clicked" handler="SHOW_TYPE" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">True</property>
                <property name="pack_type">end</property>
                <property name="position">7</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="resize">False</property>
            <property name="shrink">True</property>
          </packing>
        </child>
        <child>
          <object class="GtkScrolledWindow" id="scrolledwindow2">
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="shadow_type">in</property>
            <child>
              <object class="GtkViewport" id="viewport1">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="events">GDK_BUTTON_MOTION_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK</property>
                <child>
                  <object class="GtkImage" id="image1">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <property name="halign">center</property>
                    <property name="valign">center</property>
                    <property name="stock">gtk-missing-image</property>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="resize">True</property>
            <property name="shrink">True</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>


treeview_filechooser.py


#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
# treeview_filechosser.py
#
# Copyright (C) 2015 - root
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#給頂層視窗window參數,容器add_widget參數,和處理圖片的物件view參數,可加入TreeView到容器dd_widget
#call_resize方法中,參數view物件須提供方法i_image_resize,此方法只是來處理,TreeView選項被選取後會呼叫
#此treeview,提供File_open()來打開檔案圖片,選取後加入treeview的表單
#_type_若是"DELETE",選取file,加入treeview會先清空treeview內容,否則一直加上去
from gi.repository import Gtk, GdkPixbuf, Gdk
import sys,os
from gi.repository.GdkPixbuf import Pixbuf, InterpType
class t_filechosser():
 #建構式,用來初始化物件
 def __init__(self,add_widget,window,view,_type_,ifile):
        #inital data
        self._TYPE_=_type_
        self.add_tree_w=add_widget
        self.window=window
        self.image_viewport_new=view
        self.file=ifile
        #新Treeview,要先有store來存放資料
        self.store = Gtk.ListStore(str)
        self.tt=Gtk.TreeView(self.store)
        #Treeview加入
        self.add_tree_w.add(self.tt)
        #建立signal connect
        self.tt.connect('cursor-changed', self.SHOW_ITEM)
        #記得要給TreeViewColumn,否則TreeView無法顯示
        column = Gtk.TreeViewColumn("-----檔名-----", Gtk.CellRendererText(), text=0)
        column.set_sort_column_id(0)
        self.tt.append_column(column)


   
 def File_open(self):
        self.dlg = Gtk.FileChooserDialog(
                "Select Picture",
                self.window,
                Gtk.FileChooserAction.OPEN,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) )
        self.dlg.show()
        print("開啟檔案選擇器")
        self.dlg.set_default_response(Gtk.ResponseType.CANCEL)
        self.dlg. set_select_multiple(True)


        #可用glade加檔案過濾器
        #self.dlg.add_filter(self.filefilter1)
        #加檔案過濾器'*.jpg','*png','*PNG','*.JPG'   
        lfilter = Gtk.FileFilter()
        lfilter.set_name('Picture')
        flist=list(('*.jpg','*png','*PNG','*.JPG'))
        for fl in flist:
         lfilter.add_pattern(fl)
         print(fl)
        self.dlg.add_filter(lfilter)
        self.dlg.set_filter(lfilter)
        #設定filechosserdialog按鈕事件,按OK加入list,否則destroy
          response = self.dlg.run()
          if response == Gtk.ResponseType.OK:
         if self._TYPE_=="DELETE":self.store.clear()
         #取得選取file list 
         lfile=self.dlg.get_filenames()
           #print(lfile[0])
           #加入treeview list
           for x in lfile:
           #self.liststore1.append([x])
           self.store.append([x])
           #self.image_viewport_new.i_image_resize(lfile[0])
           self.tt.set_cursor(0)
       
         self.dlg.destroy()
          elif response == Gtk.ResponseType.CANCEL:
              print("Cancel clicked")
        #注意最後要destroy,讓使用者按左上x能關閉 
          self.dlg.destroy()                                                 
       
    #連結信號,設定顯示的樣式
 def SHOW_ITEM(self, widget,*event):
        try :
         sel=self.tt.get_selection()
         (model,iter)=sel.get_selected()
         self.file=list(model[iter])[0]
         #取得image_viewport_new物件的i_image_resize來重設圖片大小
         #self.image_viewport_new.i_image_resize(self.file)
         self.call_resize()
         print(list(model[iter])[0])
        except:pass
 def call_resize(self):
     try :
        self.image_viewport_new.i_image_resize(self.file)
     except:print("請給一個物件的方法i_image_resize,來處理TreeView選項被選取")



size_image.py



#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
#image_viewport類別,是用來重設圖片大小
#給一個image參數, viewport參數,i_type參數,產生物件,此物件提供調整圖片大小的i_image_resize方法
#image是顯示該圖片的widget
#viewport是放image的容器
#i_type給任何字串,圖片會調到和viewprt一樣大,若設定"縮放",照圖片等比例調整
#i_image_resize(self,filepath)給圖片filepath,重設圖片大小
#is_viewpot_change(self,widget)給viewport,判定viewport是否改變
#為什要判定viewport是否改變,譬如給check-resize信號用來判斷,當使用者拉大視窗一下,
#連結調整圖片,載入圖片時,會觸發check-resizecheck-resize又會呼叫調整圖片,變成迴圈

from gi.repository import Gtk, GdkPixbuf, Gdk
import sys,os
from gi.repository.GdkPixbuf import Pixbuf, InterpType
class image_viewport():
 #建構式,用來初始化物件
 def __init__(self,image, viewport,i_type):
        #inital data
        self.w_size=0
        self.h_size=0
        self.image = image
        self.viewport = viewport
        self.i_type=i_type

 #定義了一些取得設定物件成員值
 def i_get_viewport_h_size(self):
       return self.h_size
 def i_get_viewport_w_size(self):
       return self.w_size
 def i_set_show_type(self,i_type):
       self.i_type= i_type
 def i_get_show_type(self):
       return self.i_type
 def is_viewpot_change(self,widget):
        #目前viewport size
        boolean=False
        w_size=widget.get_allocation().width
        h_size=widget.get_allocation().height       
        #檢查viewport是否改變,若是回傳值True
        if(self.w_size!=w_size or self.h_size!=h_size ):
           boolean=True
        return boolean   

 #定義重設視窗的方法      
 def i_image_resize(self,filepath):
        self.h_size=self.viewport.get_allocation().height
        self.w_size=self.viewport.get_allocation().width
        #從filepath產生圖片buffer
        pixbuf = Pixbuf.new_from_file(filepath)
        #orange image size
        iw=pixbuf.get_width()
        ih=pixbuf.get_height()
        #參數先轉成float
        w,h=change_width_height(self,float(iw),float(ih),float(self.w_size),float(self.h_size))
        print("width" ,w)
        print("height",h)
       
        #重設圖片buffer寬高
        pixbuf = pixbuf.scale_simple(w, h, InterpType.BILINEAR)
        #把圖片buffer放到image widget
        self.image.set_from_pixbuf(pixbuf)
       

#定義方法,用來計算圖片新的寬高,並回傳寬和高
def change_width_height(self,ix,iy,vx,vy):

      #預設回傳viewport寬和高
      width=vx
      height=vy
      cy=iy/vy
      cx=ix/vx
      #print(cy)
      #print(self.i_type)

      #判斷image與viewport比例,顯示若是EXTEND形式,設定新的寬和高
      if cx >= cy and self.i_type=="縮放":
      
       try:
        #new size
        width=ix/cx
        height=iy/cx
       except:pass
      if cy >= cx and self.i_type=="縮放":
       try:
        #new size
        width=ix/cy
        height=iy/cy
       except:pass
      
      return width,height
      #結束def change_width_height end



 主程式pygtk_foobar_u1410.py









#!/usr/bin/env python
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
#
# main.py
# Copyright (C) 2015 yplin
#
# pygtk-foobar_u1410 is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pygtk-foobar_u1410 is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see .

from gi.repository import Gtk, GdkPixbuf, Gdk

import os, sys ,size_image,treeview_filechooser
import glob
from gi.repository.GdkPixbuf import Pixbuf, InterpType

#Comment the first line and uncomment the second before installing
#or making the tarball (alternatively, use project variables)
UI_FILE = "src/pygtk_foobar_u1410.ui"
#UI_FILE = "/usr/local/share/pygtk_foobar_u1410/ui/pygtk_foobar_u1410.ui"


class GUI:
    def __init__(self):
        #新建了一個xml-gtk
        self.builder = Gtk.Builder()
        #把UI_FILE加到gtk,glade產生的xml
        self.builder.add_from_file(UI_FILE)
        #建立xml信號連結
        self.builder.connect_signals(self)
        #取得xml UI上所有元件
        self.file="src/logo.png"
        self.test_w=0
        self.test_h=0
        self.label=self.builder.get_object('label1')
        self.button2=self.builder.get_object('button2')
        self.button1=self.builder.get_object('button1')
        self.button3=self.builder.get_object('button3')
        self.window=self.builder.get_object('window')
        self.fixed=self.builder.get_object('fixed1')
        self.image=self.builder.get_object('image1')
        self.viewport=self.builder.get_object('viewport1')
        self.treeview=self.builder.get_object('treeview1')
        self.liststore1=self.builder.get_object('liststore1')
        self.filefilter1=self.builder.get_object('filefilter1')
        self.scrolledwindow1=self.builder.get_object('scrolledwindow1')
        self.image.set_from_file(self.file)
        #self.dlg=self.builder.get_object('filechooserdialog1')
       

        #初始化
       
        #self.treeview.append_column(column)
        #初始化,產生自定義image_viewport ,t_filechosser CLASS
        self.image_viewport_new=size_image.image_viewport(self.image,self.viewport,"擴展")
        self.t_filechosser=treeview_filechooser.t_filechosser(self.scrolledwindow1,self.window,self.image_viewport_new,"DELETE",self.file)
       
    def on_window_destroy(self, widget, *event):
        Gtk.main_quit()
    def on_window_destroy1(self, widget, *event):
        print("HHHHHHHHHHH")


   
   
    #button click 連結信號   
    def SHOW(self, widget, *event):
        print("DDDD")
        #self.label.set_text(self.button2.get_label())
        try:
         #get_focus可取得激活對象
         bt_active_label=self.window.get_focus().get_label()
         #widget是指信號處理對象,用GALADE創建UI時,是為label1
         widget.set_text(bt_active_label)
        except:pass
        #除錯技巧,印出
        #重設label位置
        #self.fixed.put(self.label, 80, 40)


       
       
    #button click 連結信號       
    def SHOW_IMAGE(self, widget, *event):
        #取得激活對象按鈕名稱
        bt_active_label=self.window.get_focus().get_label()
        #根據按鈕名稱,顯示或隱藏圖片
        if bt_active_label=='隱藏圖片':widget.hide()
        if bt_active_label=='顯示圖片':widget.show()
        if bt_active_label=="離開":Gtk.main_quit()
       

    #Window chec-resize連結信號,重設圖片大小
    def IMAGE_RESIZE(self, widget, *event):
        #目前viewport size
        #w_size=self.viewport.get_allocation().width
        #h_size=self.viewport.get_allocation().height
        #存在記憶體viewport size
        #wv=self.image_viewport_new.i_get_viewport_w_size()
        #hv=self.image_viewport_new.i_get_viewport_h_size()

       
        #檢查viewport是否改變,重設size,否則成為無窮迴圈
        if self.image_viewport_new.is_viewpot_change(widget) :
           #print(str(h_size))
           #resize方法,來重設大小方式
           self.t_filechosser.call_resize()

    #連結信號,設定顯示的樣式
    def SHOW_TYPE(self, *event):
        b_label=self.window.get_focus().get_label()
        self.image_viewport_new.i_set_show_type(b_label)
        #resize方法,重設大小方式
        self.t_filechosser.call_resize()
       
        print(0 > 3)
       
    def IMAGE_VIEWER(self, *event):
       
        self.t_filechosser.File_open()
       
       
def main():
    app = GUI()
    app.window.show_all()
    Gtk.main()
   
   
if __name__ == "__main__":
    sys.exit(main())

     

read more...