1. 程式人生 > >pygame學習教程(四)螢幕顯示多個按鈕

pygame學習教程(四)螢幕顯示多個按鈕

前一篇
說明我寫這個系列是為了給初學者展示一些思路和技巧,很多程式碼不是最優的。如果有朋友對構架有不同看法,歡迎指正。
這裡,繼續上個例子展示一些python的技巧。
首先,我們修改Jbutton()類。需要涉及很多的向量計算,這裡引入
DVerctor。需要將這個檔案考到執行檔案相同的目錄,import。
DVerctor.py ,可以執行測試一下

################## http://www.pygame.org/wiki/2DVectorClass ##################
import operator
import math

class Vec2d(object):
    """2d vector class, supports vector and scalar operators,
       and also provides a bunch of high level functions
       """
    __slots__ = ['x', 'y']

    def __init__(self, x_or_pair, y = None):
        if y == None:
            self.x = x_or_pair[0]
            self.y = x_or_pair[1]
        else:
            self.x = x_or_pair
            self.y = y

    def __len__(self):
        return 2

    def __getitem__(self, key):
        if key == 0:
            return self.x
        elif key == 1:
            return self.y
        else:
            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")

    def __setitem__(self, key, value):
        if key == 0:
            self.x = value
        elif key == 1:
            self.y = value
        else:
            raise IndexError("Invalid subscript "+str(key)+" to Vec2d")

    # String representaion (for debugging)
    def __repr__(self):
        return 'Vec2d(%s, %s)' % (self.x, self.y)

    # Comparison
    def __eq__(self, other):
        if hasattr(other, "__getitem__") and len(other) == 2:
            return self.x == other[0] and self.y == other[1]
        else:
            return False

    def __ne__(self, other):
        if hasattr(other, "__getitem__") and len(other) == 2:
            return self.x != other[0] or self.y != other[1]
        else:
            return True

    def __nonzero__(self):
        return bool(self.x or self.y)

    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a Vec2d"
        if isinstance(other, Vec2d):
            return Vec2d(f(self.x, other.x),
                         f(self.y, other.y))
        elif (hasattr(other, "__getitem__")):
            return Vec2d(f(self.x, other[0]),
                         f(self.y, other[1]))
        else:
            return Vec2d(f(self.x, other),
                         f(self.y, other))

    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a Vec2d"
        if (hasattr(other, "__getitem__")):
            return Vec2d(f(other[0], self.x),
                         f(other[1], self.y))
        else:
            return Vec2d(f(other, self.x),
                         f(other, self.y))

    def _io(self, other, f):
        "inplace operator"
        if (hasattr(other, "__getitem__")):
            self.x = f(self.x, other[0])
            self.y = f(self.y, other[1])
        else:
            self.x = f(self.x, other)
            self.y = f(self.y, other)
        return self

    # Addition
    def __add__(self, other):
        if isinstance(other, Vec2d):
            return Vec2d(self.x + other.x, self.y + other.y)
        elif hasattr(other, "__getitem__"):
            return Vec2d(self.x + other[0], self.y + other[1])
        else:
            return Vec2d(self.x + other, self.y + other)
    __radd__ = __add__

    def __iadd__(self, other):
        if isinstance(other, Vec2d):
            self.x += other.x
            self.y += other.y
        elif hasattr(other, "__getitem__"):
            self.x += other[0]
            self.y += other[1]
        else:
            self.x += other
            self.y += other
        return self

    # Subtraction
    def __sub__(self, other):
        if isinstance(other, Vec2d):
            return Vec2d(self.x - other.x, self.y - other.y)
        elif (hasattr(other, "__getitem__")):
            return Vec2d(self.x - other[0], self.y - other[1])
        else:
            return Vec2d(self.x - other, self.y - other)
    def __rsub__(self, other):
        if isinstance(other, Vec2d):
            return Vec2d(other.x - self.x, other.y - self.y)
        if (hasattr(other, "__getitem__")):
            return Vec2d(other[0] - self.x, other[1] - self.y)
        else:
            return Vec2d(other - self.x, other - self.y)
    def __isub__(self, other):
        if isinstance(other, Vec2d):
            self.x -= other.x
            self.y -= other.y
        elif (hasattr(other, "__getitem__")):
            self.x -= other[0]
            self.y -= other[1]
        else:
            self.x -= other
            self.y -= other
        return self

    # Multiplication
    def __mul__(self, other):
        if isinstance(other, Vec2d):
            return Vec2d(self.x*other.x, self.y*other.y)
        if (hasattr(other, "__getitem__")):
            return Vec2d(self.x*other[0], self.y*other[1])
        else:
            return Vec2d(self.x*other, self.y*other)
    __rmul__ = __mul__

    def __imul__(self, other):
        if isinstance(other, Vec2d):
            self.x *= other.x
            self.y *= other.y
        elif (hasattr(other, "__getitem__")):
            self.x *= other[0]
            self.y *= other[1]
        else:
            self.x *= other
            self.y *= other
        return self

    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)
    def __idiv__(self, other):
        return self._io(other, operator.div)

    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)
    def __ifloordiv__(self, other):
        return self._io(other, operator.floordiv)

    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)
    def __itruediv__(self, other):
        return self._io(other, operator.floordiv)

    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)

    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)

    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)

    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)

    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)

    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__

    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__

    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__

    # Unary operations
    def __neg__(self):
        return Vec2d(operator.neg(self.x), operator.neg(self.y))

    def __pos__(self):
        return Vec2d(operator.pos(self.x), operator.pos(self.y))

    def __abs__(self):
        return Vec2d(abs(self.x), abs(self.y))

    def __invert__(self):
        return Vec2d(-self.x, -self.y)

    # vectory functions
    def get_length_sqrd(self):
        return self.x**2 + self.y**2

    def get_length(self):
        return math.sqrt(self.x**2 + self.y**2)
    def __setlength(self, value):
        length = self.get_length()
        self.x *= value/length
        self.y *= value/length
    length = property(get_length, __setlength, None, "gets or sets the magnitude of the vector")

    def rotate(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.x*cos - self.y*sin
        y = self.x*sin + self.y*cos
        self.x = x
        self.y = y

    def rotated(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.x*cos - self.y*sin
        y = self.x*sin + self.y*cos
        return Vec2d(x, y)

    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self.y, self.x))
    def __setangle(self, angle_degrees):
        self.x = self.length
        self.y = 0
        self.rotate(angle_degrees)
    angle = property(get_angle, __setangle, None, "gets or sets the angle of a vector")

    def get_angle_between(self, other):
        cross = self.x*other[1] - self.y*other[0]
        dot = self.x*other[0] + self.y*other[1]
        return math.degrees(math.atan2(cross, dot))

    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return Vec2d(self)

    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self.x /= length
            self.y /= length
        return length

    def perpendicular(self):
        return Vec2d(-self.y, self.x)

    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return Vec2d(-self.y/length, self.x/length)
        return Vec2d(self)

    def dot(self, other):
        return float(self.x*other[0] + self.y*other[1])

    def get_distance(self, other):
        return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)

    def get_dist_sqrd(self, other):
        return (self.x - other[0])**2 + (self.y - other[1])**2

    def projection(self, other):
        other_length_sqrd = other[0]*other[0] + other[1]*other[1]
        projected_length_times_other_length = self.dot(other)
        return other*(projected_length_times_other_length/other_length_sqrd)

    def cross(self, other):
        return self.x*other[1] - self.y*other[0]

    def interpolate_to(self, other, range):
        return Vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*range)

    def convert_to_basis(self, x_vector, y_vector):
        return Vec2d(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())

    def __getstate__(self):
        return [self.x, self.y]

    def __setstate__(self, dict):
        self.x, self.y = dict

########################################################################
## Unit Testing                                                       ##
########################################################################
if __name__ == "__main__":

    import unittest
    import pickle

    ####################################################################
    class UnitTestVec2D(unittest.TestCase):

        def setUp(self):
            pass

        def testCreationAndAccess(self):
            v = Vec2d(111,222)
            self.assertTrue(v.x == 111 and v.y == 222)
            v.x = 333
            v[1] = 444
            self.assertTrue(v[0] == 333 and v[1] == 444)

        def testMath(self):
            v = Vec2d(111,222)
            self.assertEqual(v + 1, Vec2d(112,223))
            self.assertTrue(v - 2 == [109,220])
            self.assertTrue(v * 3 == (333,666))
            self.assertTrue(v / 2.0 == Vec2d(55.5, 111))
            self.assertTrue(v / 2 == (55.5, 111))
            self.assertTrue(v ** Vec2d(2,3) == [12321, 10941048])
            self.assertTrue(v + [-11, 78] == Vec2d(100, 300))
            self.assertTrue(v / [10,2] == [11.1,111])

        def testReverseMath(self):
            v = Vec2d(111,222)
            self.assertTrue(1 + v == Vec2d(112,223))
            self.assertTrue(2 - v == [-109,-220])
            self.assertTrue(3 * v == (333,666))
            self.assertTrue([222,888] / v == [2,4])
            self.assertTrue([111,222] ** Vec2d(2,3) == [12321, 10941048])
            self.assertTrue([-11, 78] + v == Vec2d(100, 300))

        def testUnary(self):
            v = Vec2d(111,222)
            v = -v
            self.assertTrue(v == [-111,-222])
            v = abs(v)
            self.assertTrue(v == [111,222])

        def testLength(self):
            v = Vec2d(3,4)
            self.assertTrue(v.length == 5)
            self.assertTrue(v.get_length_sqrd() == 25)
            self.assertTrue(v.normalize_return_length() == 5)
            self.assertTrue(v.length == 1)
            v.length = 5
            self.assertTrue(v == Vec2d(3,4))
            v2 = Vec2d(10, -2)
            self.assertTrue(v.get_distance(v2) == (v - v2).get_length())

        def testAngles(self):
            v = Vec2d(0, 3)
            self.assertEqual(v.angle, 90)
            v2 = Vec2d(v)
            v.rotate(-90)
            self.assertEqual(v.get_angle_between(v2), 90)
            v2.angle -= 90
            self.assertEqual(v.length, v2.length)
            self.assertEqual(v2.angle, 0)
            self.assertEqual(v2, [3, 0])
            self.assertTrue((v - v2).length < .00001)
            self.assertEqual(v.length, v2.length)
            v2.rotate(300)
            self.assertAlmostEqual(v.get_angle_between(v2), -60)
            v2.rotate(v2.get_angle_between(v))
            angle = v.get_angle_between(v2)
            self.assertAlmostEqual(v.get_angle_between(v2), 0)

        def testHighLevel(self):
            basis0 = Vec2d(5.0, 0)
            basis1 = Vec2d(0, .5)
            v = Vec2d(10, 1)
            self.assertTrue(v.convert_to_basis(basis0, basis1) == [2, 2])
            self.assertTrue(v.projection(basis0) == (10, 0))
            self.assertTrue(basis0.dot(basis1) == 0)

        def testCross(self):
            lhs = Vec2d(1, .5)
            rhs = Vec2d(4,6)
            self.assertTrue(lhs.cross(rhs) == 4)

        def testComparison(self):
            int_vec = Vec2d(3, -2)
            flt_vec = Vec2d(3.0, -2.0)
            zero_vec = Vec2d(0, 0)
            self.assertTrue(int_vec == flt_vec)
            self.assertTrue(int_vec != zero_vec)
            self.assertTrue((flt_vec == zero_vec) == False)
            self.assertTrue((flt_vec != int_vec) == False)
            self.assertTrue(int_vec == (3, -2))
            self.assertTrue(int_vec != [0, 0])
            self.assertTrue(int_vec != 5)
            self.assertTrue(int_vec != [3, -2, -5])

        def testInplace(self):
            inplace_vec = Vec2d(5, 13)
            inplace_ref = inplace_vec
            inplace_src = Vec2d(inplace_vec)
            inplace_vec *= .5
            inplace_vec += .5
            inplace_vec /= (3, 6)
            inplace_vec += Vec2d(-1, -1)
            self.assertEqual(inplace_vec, inplace_ref)

        def testPickle(self):
            testvec = Vec2d(5, .3)
            testvec_str = pickle.dumps(testvec)
            loaded_vec = pickle.loads(testvec_str)
            self.assertEqual(testvec, loaded_vec)

    ####################################################################
    unittest.main()

我的思路是把Jbutton設計成一個可以移動的按鈕,這樣需要Move等其它的方法,我想我一定需要一些不需要移動的窗體,需要區分。因為例項了一個類。就會繼承這些屬性,方法。在很多語言裡,都是給你封裝好,你不用也沒有優化多少,當然,很多人認為這些資源無所謂。但是專案越大,越應該有資源的意識。所以在這裡建立

class JCon():
    def __init__(self,vertex,mouse_image_filename):
        self.vertex=vertex                         #設定按鈕頂點 set button vertex (left,top)格式
        self.mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
        self.count=0                                #用於計數
        self.BoPo =DVerctor.Vec2d(vertex)+DVerctor.Vec2d(self.mouse_cursor.get_width(),self.mouse_cursor.get_height())
        #獲得範圍left+width,top+height (x,y)+(x1,y1)
    def SetPo(self):  #設定位置 set position  #
        screen.blit(self.mouse_cursor,self.vertex)
    def Mouse_Click(self):
        pass

class Jbutton(JCon):
    pass

基於按鈕類會判斷範圍,會繪製圖形,都需要定義一個滑鼠事件(假定)Jbutton 繼承JCon

# coding: utf8
import pygame
#匯入pygame庫
from pygame.locals import *
#匯入一些常用的函式和常量
from sys import exit
import  pickle
from PIL import Image
import DVerctor
# coding: utf8
import pygame
#匯入pygame庫
from pygame.locals import *
#匯入一些常用的函式和常量
from sys import exit
import  pickle
import DVerctor

ButtonDict={}
class JCon():
    def __init__(self,vertex,mouse_image_filename):
        self.vertex=vertex                         #設定按鈕頂點 set button vertex (left,top)格式
        self.mouse_cursor = pygame.image.load(mouse_image_filename).convert_alpha()
        self.count=0                                #用於計數
        self.BoPo =DVerctor.Vec2d(vertex)+DVerctor.Vec2d(self.mouse_cursor.get_width(),self.mouse_cursor.get_height())
        #獲得範圍left+width,top+height (x,y)+(x1,y1)
    def SetPo(self):  #設定位置 set position  #
        screen.blit(self.mouse_cursor,self.vertex)
    def Mouse_Click(self):
        pass

class Jbutton(JCon):
    pass
def TextPygame():  #測試模組是否存在和版本號
    print(pygame.ver)
    pgname = ['pygame.cdrom', 'pygame.cursors', 'pygame.display', 'pygame.draw',
              'pygame.event', 'pygame.font', 'pygame.image', 'pygame.joystick',
              'pygame.key', 'pygame.mixer', 'pygame.mouse', 'pygame.movie', 'pygame.music',
              'pygame.overlay', 'pygame', 'pygame.rect', 'pygame.sndarray', 'pygame.sprite',
              'pygame.surface', 'pygame.surfarray', 'pygame.time']
    for i in pgname:
        if i is None:
            print(i+" is None")
        else:
            print(i + " at this computer")

def storeTree(filename,*args):
    with open(filename,'wb') as fw:  #開啟需要用'wb'
        for i in args:
            pickle.dump(i, fw,-1) #為了保護資料protocol=-1,設為0可以看到資料

def grabTree(filename):
    Mylist=[]   #返回變數的列表
    with open(filename,'rb') as fr:
        while True:        #這裡用try最簡單,不用定義迴圈次數
            try:
                Mylist.append(pickle.load(fr))
            except:
                break
    return Mylist

if __name__ == "__main__":
    background_image_filename = 'sushiplate.jpg'
    # 指定影象檔名稱
    pygame.init()
    # 初始化pygame,為使用硬體做準備
    screen = pygame.display.set_mode((640, 480), 0, 32)
    # 建立了一個視窗
    pygame.display.set_caption("Hello, World!")    # 設定視窗標題
    background = pygame.image.load(background_image_filename).convert()
    #在這裡新增按鈕


    ButtonDict["butto1"] = [(12, 13), 'feid1.png']  #全域性變數,判定
    ButtonDict["butto2"] = [(52, 73), 'feid1.png']
    ButtonDict["butto3"] = [(102, 203), 'feid1.png']

    butto1 = Jbutton((12, 13), 'feid1.png')
    butto2 = Jbutton((52, 73), 'feid1.png')
    butto3 = Jbutton((102, 203), 'feid1.png')
    #這裡的意思是初始化完成根據字典判斷按鈕的位置,實際上可以用按鈕的屬性高判斷
    ButtonDict["butto1"] = [ButtonDict["butto1"][0], (butto1.BoPo[0], butto1.BoPo[1])]
    ButtonDict["butto2"] = [ButtonDict["butto2"][0], (butto2.BoPo[0], butto2.BoPo[1])]
    ButtonDict["butto3"] = [ButtonDict["butto3"][0], (butto3.BoPo[0], butto3.BoPo[1])]


    print("ButtonDict=",ButtonDict)
    #修改ButtonDict
    while True:
        # 遊戲主迴圈
        for event in pygame.event.get():
            if event.type == QUIT:
                # 接收到退出事件後退出程式
                exit()

        screen.blit(background, (0, 0))
        # 將背景圖畫上去
        x, y = pygame.mouse.get_pos()
        # 獲得滑鼠位置
        # 計算游標的左上角位置

        #screen.blit(mouse_cursor, (x, y))
        # 把游標畫上去
        butto1.SetPo()
        butto2.SetPo()
        butto3.SetPo()
        #screen.blit(mouse_cursor, butto1.vertex)
        pygame.display.update()
        # 重新整理一下畫面




這段程式碼怎摸樣,

  ButtonDict["butto1"] = [(12, 13), 'feid1.png']  #全域性變數,判定
    ButtonDict["butto2"] = [(52, 73), 'feid1.png']
    ButtonDict["butto3"] = [(102, 203), 'feid1.png']

    butto1 = Jbutton((12, 13), 'feid1.png')
    butto2 = Jbutton((52, 73), 'feid1.png')
    butto3 = Jbutton((102, 203), 'feid1.png')
    #這裡的意思是初始化完成根據字典判斷按鈕的位置,實際上可以用按鈕的屬性高判斷
    ButtonDict["butto1"] = [ButtonDict["butto1"][0], (butto1.BoPo[0], butto1.BoPo[1])]
    ButtonDict["butto2"] = [ButtonDict["butto2"][0], (butto2.BoPo[0], butto2.BoPo[1])]
    ButtonDict["butto3"] = [ButtonDict["butto3"][0], (butto3.BoPo[0], butto3.BoPo[1])]

很不客氣說,很討厭。臃腫乏味,關鍵就是你新增一個按鈕就要改很多地方,這非常討厭。還有這末寫ButtonDict[“butto1”] = [(12, 13), ‘feid1.png’] 為了程式碼不要過長,很容易搞錯,如果這末寫多些幾個按鈕會讓你懷疑人生。這些都是最壞程式碼的例子,下一章我們優化它。

ButtonDict = {"butto1": [(12, 13), 'feid1.png'], "butto2": [(52, 73), 'feid1.png'], "butto3": [(102, 203), 'feid1.png']}   

後一篇