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

沒有留言: