图形用户界面和游戏开发

主要介绍下tkinterPygame 两个模块, 真搞gui有用专业点的,比如 wxPythonPyQtPyGTK之类的,学习pygame好好理解面向对象变成,真想搞游戏也是可以的,就得往细致了研究

wxpython 可以看这一篇https://www.cnblogs.com/morries123/p/8568666.html

1. 基于tkinter模块的GUI

GUI是图形用户界面的缩写,图形化的用户界面对使用过计算机的人来说应该都不陌生,在此也无需进行赘述。Python默认的GUI开发模块是tkinter(在Python 3以前的版本中名为Tkinter),从这个名字就可以看出它是基于Tk的,Tk是一个工具包,最初是为Tcl设计的,后来被移植到很多其他的脚本语言中,它提供了跨平台的GUI控件。

基本上使用tkinter来开发GUI应用需要以下5个步骤:

    1. 导入tkinter模块中我们需要的东西。
    1. 创建一个顶层窗口对象并用它来承载整个GUI应用。
    1. 在顶层窗口对象上添加GUI组件。
    1. 通过代码将这些GUI组件的功能组织起来。
    1. 进入主事件循环(main loop)。

大概看下,后面再专门搞个独立分支研究gui:

# import tkinter
# tk = tkinter.Tk()
# tk.title('test jin')
#
# lan = ['c','c++','python','php']
# movie = ['css','jq','bt']
#
# lista = tkinter.Listbox(tk)
# listb = tkinter.Listbox(tk)
#
# for item in lan:
#     lista.insert(0,item)
#
# for item in movie:
#     listb.insert(0,item)
#
# label = tkinter.Label(tk,{'text' :'hello'})
#
#
# lista.pack()
# listb.pack()
# label.pack()
#
#
# tk.mainloop()

import tkinter
import tkinter.messagebox


def main():
    flag = True

    # 修改标签上的文字
    def change_label_text():
        nonlocal flag
        flag = not flag
        color, msg = ('red', 'Hello, world!')\
            if flag else ('blue', 'Goodbye, world!')
        label.config(text=msg, fg=color)

    # 确认退出
    def confirm_to_quit():
        if tkinter.messagebox.askokcancel('温馨提示', '确定要退出吗?'):
            top.quit()

    # 创建顶层窗口
    top = tkinter.Tk()
    # 设置窗口大小
    top.geometry('240x160')
    # 设置窗口标题
    top.title('小游戏')
    # 创建标签对象并添加到顶层窗口
    label = tkinter.Label(top, text='Hello, world!', font='Arial -32', fg='red')
    label.pack(expand=1)
    # 创建一个装按钮的容器
    panel = tkinter.Frame(top)
    # 创建按钮对象 指定添加到哪个容器中 通过command参数绑定事件回调函数
    button1 = tkinter.Button(panel, text='修改', command=change_label_text)
    button1.pack(side='left')
    button2 = tkinter.Button(panel, text='退出', command=confirm_to_quit)
    button2.pack(side='right')
    panel.pack(side='bottom')
    # 开启主事件循环
    tkinter.mainloop()


if __name__ == '__main__':
    main()

效果:
day-012--图形用户界面和游戏开发-LMLPHP

2.使用Pygame进行游戏开发

这段直接搬作者的了,主要学习思想

Pygame是一个开源的Python模块,专门用于多媒体应用(如电子游戏)的开发,其中包含对图像、声音、视频、事件、碰撞等的支持。Pygame建立在SDL的基础上,SDL是一套跨平台的多媒体开发库,用C语言实现,被广泛的应用于游戏、模拟器、播放器等的开发。而Pygame让游戏开发者不再被底层语言束缚,可以更多的关注游戏的功能和逻辑。

大神给的例子是大球吃小球,我改了部分bug,加了点东西进去,

from enum import Enum, unique
from math import sqrt,cos,sin,pi
from random import randint
import easygui as g
import pygame


class BallType:
    normal,diminish,split,hero = range(4)


class Direction:
    left, ltop, top, rtop,right, rbottom, bottom, lbottom, nothing = range(9)
    dict_direct = {
        left: [(10, 170),False],
        ltop: [(10, 80), True],
        top: [(10, 170), True],
        rtop: [(100, 170),True],
        right: [(190, 350), False],
        rbottom: [(170, 260), True],
        bottom: [(190, 350), True],
        lbottom: [(280, 350), True],
        nothing: [(0, 360), True]
    }


def random_ball(balltype,x,y,radius):
    sx, sy = randint(-10, 10), randint(-10, 10)
    color = Color.random_color()
    # 在点击鼠标的位置创建一个球(大小、速度和颜色随机)
    if balltype is BallType.hero:
        global hero_ball
        hero_ball = Ball(balltype, x, y, radius, sx, sy, color)
        balls.append(hero_ball)
    else:
        ball = Ball(balltype, x, y, radius, sx, sy, color)
        balls.append(ball)
    # 将球添加到列表容器中
    # balls.append(ball)


@unique
class Color(Enum):
    """颜色"""

    RED = (255, 0, 0)
    GREEN = (0, 255, 0)
    BLUE = (0, 0, 255)
    BLACK = (0, 0, 0)
    WHITE = (255, 255, 255)
    GRAY = (242, 242, 242)

    @staticmethod
    def random_color():
        """获得随机颜色"""
        r = randint(0, 255)
        g = randint(0, 255)
        b = randint(0, 255)
        return (r, g, b)


class Ball(object):
    """球"""
    dirchange = True
    direction = 8;
    go_direction = 8;
    direction_x = 0
    direction_y = 0
    def __init__(self, balltype, x, y, radius, sx, sy, color=Color.RED):
        """初始化方法"""
        self.x = x
        self.y = y
        self.radius = radius
        self.sx = sx
        self.sy = sy
        self.color = color
        self.alive = True
        self.balltype = balltype

    def do_direction(self,screen):
        degree = randint(Direction.dict_direct[self.direction][0][0],Direction.dict_direct[self.direction][0][1])
        if Direction.dict_direct[self.direction][1]:
            degree = (degree / 180 * pi)
            self.direction_x = cos(degree)
            self.direction_y = sin(degree)
        else:
            degree = (degree / 180 * pi)
            self.direction_x = sin(degree)
            self.direction_y = cos(degree)

        self.go_direction = self.direction

    def move(self, screen):
        """移动"""
        if self.dirchange:
            self.dirchange = False
            self.do_direction(screen)

        maxspeed = 900/self.radius
        if maxspeed > 30:
            maxspeed = 30
        self.x += int(maxspeed * self.direction_x)
        self.y += int(maxspeed * self.direction_y)

        if self.x - self.radius <= 0 and self.y - self.radius <= 0:  # ltop
            if self.go_direction is not Direction.ltop:
                self.dirchange = True
                self.direction = Direction.ltop

        elif self.x - self.radius > 0 >= self.y - self.radius and self.x + self.radius < screen.get_width():  # top
            if self.go_direction is not Direction.top:
                self.dirchange = True
                self.direction = Direction.top

        elif  self.x + self.radius >= screen.get_width() and self.y - self.radius <= 0:  # rtop
            if self.go_direction is not Direction.rtop:
                self.dirchange = True
                self.direction = Direction.rtop

        elif self.x + self.radius >= screen.get_width() and self.y+self.radius < screen.get_height() and self.y - self.radius > 0:  # right
            if self.go_direction is not Direction.right:
                self.dirchange = True
                self.direction = Direction.right

        elif self.x + self.radius >= screen.get_width() and self.y + self.radius >= screen.get_height():  # rbottom
            if self.go_direction is not Direction.rbottom:
                self.dirchange = True
                self.direction = Direction.rbottom

        elif self.x - self.radius > 0 and self.x + self.radius < screen.get_width() and self.y + self.radius >= screen.get_height():  # bottom
            if self.go_direction is not Direction.bottom:
                self.dirchange = True
                self.direction = Direction.bottom

        elif self.x - self.radius <= 0 and self.y + self.radius >= screen.get_height(): # lbottom
            if self.go_direction is not Direction.lbottom:
                self.dirchange = True
                self.direction = Direction.lbottom

        elif self.x - self.radius <= 0 < self.y - self.radius and self.y + self.radius < screen.get_height():  # left
            if self.go_direction is not Direction.left:
                self.dirchange = True
                self.direction = Direction.left

    def eat(self, other):
        """吃其他球"""

        if self.alive and other.alive and self != other:
            dx, dy = self.x - other.x, self.y - other.y
            distance = sqrt(dx ** 2 + dy ** 2)
            if distance < max(self.radius, other.radius )\
                    and self.radius > other.radius:
                if other.balltype is BallType.normal or other.balltype is BallType.hero:
                    self.radius = self.radius + int(other.radius * 0.146)
                    if self.radius > 300:
                        self.radius = 300
                elif other.balltype is BallType.diminish:
                    self.diminish()
                elif other.balltype is BallType.split:
                    self.split()

                other.alive = False


    def draw(self, screen):
        """在窗口上绘制球"""
        # 不能让hero 出屏
        if self.balltype is BallType.hero:
            if self.x <= 0:
                self.x = 0
            if self.y <= 0:
                self.y = 0
            if self.x >= screen.get_width():
                self.x = screen.get_width()
            if self.y >= screen.get_height():
                self.y = screen.get_height()

        if self.balltype is BallType.normal or self.balltype is BallType.hero:
            pygame.draw.circle(screen, self.color,
                           (self.x, self.y), int(self.radius), 0)
        elif self.balltype is BallType.diminish:
            pygame.draw.rect(screen,self.color,[self.x, self.y, 20,20])
        elif self.balltype is BallType.split:
            pygame.draw.ellipse(screen,self.color,[self.x, self.y, 20, 10])

    def diminish(self):
        if self.radius > 30:
            self.radius = int(self.radius/2)

    def split(self):
        if self.radius > 50:
            radius_tmp = self.radius
            if self.balltype is BallType.hero:
                self.radius = int(self.radius/2)
                if self.radius < 30:
                    self.radius = 30
            else:
                self.radius = int(self.radius/5)
                self.alive = False

            for index in range(0,20):
                degree = 18*index/180*pi
                x = self.radius*cos(degree) + self.x
                y = self.radius*sin(degree) + self.y
                random_ball(BallType.normal,int(x),int(y),self.radius)
                # radius = self.radius
                # color = self.color
                # sx = x
                # sy = y
                # ball = Ball(x, y, radius, sx, sy, color)
                # balls.append(ball)
            if self.balltype is not BallType.hero:
                balls.remove(self)


def main():
    # 定义用来装所有球的容器
    global balls
    balls = []
    # global hero_ball


    ishavehero = False
    ishavehero_old = False
    # hero_ball = Ball()
    # global hero_ball_x
    # global hero_ball_y
    # 初始化导入的pygame中的模块
    pygame.init()
    # 初始化用于显示的窗口并设置窗口尺寸
    screen = pygame.display.set_mode((1800, 900))

    # print(screen.get_height())
    # 设置当前窗口的标题
    pygame.display.set_caption('大球吃小球')
    running = True
    sum_palce = 0
    auto_out_cnt = 0
    msg = ''
    bFail = False
    # 开启一个事件循环处理发生的事件

    # random_ball(BallType.hero, randint(100, 500), randint(100, 500), randint(10, 100))
    while running:
        # 从消息队列中获取事件并对事件进行处理
        auto_out_cnt += 1
        if bFail :
            bFail = False
            button_choices = g.buttonbox(msg, '游客', choices=('继续', '退出'))
            if button_choices == '继续':
                balls.clear()
                ishavehero = False
                msg = ''
                ishavehero_old = False
                continue
            elif button_choices == '退出':
                return 0
        if auto_out_cnt >= 20:
            if len(balls) < 30:
                random_ball(BallType.normal, randint(100, 500, ), randint(100, 500), randint(10, 100))
            auto_out_cnt = 0
            if ishavehero:
                if sum_palce > screen.get_height() * screen.get_width() / 4 \
                    or hero_ball.radius * hero_ball.radius * pi > screen.get_height() * screen.get_width() / 4:
                    if sum_palce < hero_ball.radius * hero_ball.radius * pi:
                        msg = '你赢了'
                    else:
                        msg = '你输了'
                        ishavehero = False
                    bFail = True
            elif ishavehero is False and ishavehero_old is True:
                msg = '你输了'
                bFail = True
            if  ishavehero_old is True:
                if hero_ball.alive is False:
                    msg = '你输了'
                    bFail = True


        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            # 处理鼠标事件的代码
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                # 获得点击鼠标的位置
                x,y = event.pos
                # hero_ball.
                # random_ball(BallType.normal, x, y,randint(10, 100))
                if ishavehero is False:
                    random_ball(BallType.hero, x, y, randint(50, 100))
                    random_ball(BallType.split, randint(100, 500), randint(100, 500), randint(10, 100))
                    random_ball(BallType.diminish, randint(100, 500), randint(100, 500), randint(10, 100))
                    ishavehero = True
                    ishavehero_old = True
            if ishavehero :
                hero_ball.x,hero_ball.y = pygame.mouse.get_pos()

        screen.fill((255, 255, 255))
        # 取出容器中的球 如果没被吃掉就绘制 被吃掉了就移除
        sum_palce = 0
        for ball in balls:
            if ball.balltype is BallType.normal:
                sum_palce += ball.radius*ball.radius*pi
            if ball.alive:
                ball.draw(screen)
            else:
                if ball.balltype is not BallType.normal:
                    random_ball(ball.balltype,randint(100, 500,), randint(100, 500),randint(10, 100))
                balls.remove(ball)

        pygame.display.flip()
        # 每隔50毫秒就改变球的位置再刷新窗口
        pygame.time.delay(50)



        for ball in balls:
            if ball.balltype is BallType.normal:
                ball.move(screen)
            # 检查球有没有吃到其他的球
            for other in balls:
                if ball.balltype is BallType.normal or ball.balltype is BallType.hero:
                    ball.eat(other)
        for ball in balls:
            if ball.alive:
                ball.draw(screen)
            else:
                if ball.balltype is not BallType.normal and ball.balltype is not BallType.hero:
                    random_ball(ball.balltype, randint(100, 500, ), randint(100, 500), randint(10, 100))
                balls.remove(ball)

def printsin(degree):
    print('sin(',degree,') = ',sin(degree/180*pi))

def printcos(degree):
    print('cos(', degree, ') = ', cos(degree / 180 * pi))

def printsc(degree):
    printcos(degree)
    printsin(degree)

if __name__ == '__main__':
    while True:
        ret = main()
        if ret is 0:
            exit(0)

效果:
day-012--图形用户界面和游戏开发-LMLPHP

目前是点击之后可以出一个hero角色,然后又矩形吃了会变小,椭圆吃了会分裂,达到北京四分之一就结束了,回去重新搞一下,加点分裂之后还是跟着主体,然后让这东东智能点,变成策略游戏

文集传送门 学习python100天


整个学习python100天的目录传送门


无敌分割线


再最后面附上大神的链接
传送门

07-22 01:02