pygame學習教程(四)螢幕顯示多個按鈕
阿新 • • 發佈:2018-12-17
前一篇
說明我寫這個系列是為了給初學者展示一些思路和技巧,很多程式碼不是最優的。如果有朋友對構架有不同看法,歡迎指正。
這裡,繼續上個例子展示一些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']}