在pygame中实现菜单功能,支持鼠标和键盘操作

python中的pygame库,并没有传统菜单功能,正好手边有一自己想做的小项目,需要在pygame中实现菜单功能。现将其中实现菜单功能的约400行代码摘取出来,供大家参考。

该菜单完全依托pygame实现,没有借用任何其它库。实现效果如下:

 菜单中定义了主题,可以按空格键改变,为简化,只定义了2个主题,即深色主题(见上图)和浅色主题(见下图):

 点选菜单后,程序会执行相应代码,下图为点“圆”后在屏幕输出的一个随机绘制圆的小程序,点其它菜单项其它均用print()提示: 

 下面提交代码,并进行简要说明。程序用到的库有:

from sys import  exit
import pygame
from pygame.locals import *
from random import randint
from math import floor

定义一个class:

class mymenu(object):     # 在pygame中定义菜单,支持鼠标和键盘操作

主程序定义了几个全局变量,是对一些可调整的基本量进行定义,比如菜单栏的高度、状态栏的高度、颜色等,方便使用者修改,一些变量使用了中文,我觉得用中文做变量或过程名称,可以少写许多中文注释,所以有些地方就直接用中文了。

# 主程序 ===========================
def main(NoWelcom=False):
    global screen_w,screen_h,底部状态栏高度 , 菜单栏高度 , screen,菜单 ,字体,背景颜色
    pygame.init()
    pygame.display.set_caption('Pygame菜单演示')
    字体='simsun'
    底部状态栏高度 = 22
    菜单栏高度 = 22
    背景颜色 = (255,255,255)
    改变窗口大小((1000, 800),RESIZABLE)
    菜单=mymenu() # 初始化顶部菜单

菜单项全部定义成了变量,当然,如果需要的话,可以自己改为传递参数也行。

菜单分隔条,用 '-'号表示

菜单项的宽度会根据字体大小和文字多少自动调整。演示中定义的变量如下:

    def __init__(self):
        self.menu_height=菜单栏高度
        self.sub_top_height=12                   # 子菜单第一行上边保留空间
        self.default_fontsize=16                 # 菜单初始字体大小
        self.mainfontsize=self.default_fontsize  # 主菜单字体大小
        self.subfontsize =self.default_fontsize  # 子菜单字体大小 当菜单高度大于窗口高度时,字体会自动缩小,以适应窗口
        self.menus    =[ '文件(F)','直角坐标函数展示(R)','极坐标函数展示(P)','功能(S)','趣味(Q)','帮助(H)'] # 第一级菜单 即主菜单
        self.submenus =[['新建...       Ctrl+N','修改...','-','打开文件...   Ctrl+O','保存到文件... Ctrl+S','保存图片... ','-','打开内置函数...','-','退出          Alt+F4'],
                        ['圆','椭圆','抛物线','正弦曲线','正切曲线','两个方程求共解','-','锯齿波','方波','-','笑脸','美之心(纹)','五星红旗'],
                        ['阿基米德螺线','伯努利双扭线','笛卡尔心形线','-','回旋','绳结','花心','-','模拟表盘','扫描','绚丽图形','头结','蝴蝶曲线'],
                        ['改变菜单主题     空格','-','坐标系...          F9','-','绘图方式...      1234','画笔颜色...       F10','-','切换背景图片       F6','图例设置...','-','屏幕大小...       F12','-','更多设置...'],
                        ['天体运行模拟系统','导弹模拟系统','宇宙大爆炸模拟系统','-','三体游戏','魂斗罗游戏','-','屏幕爬虫','彩虹漩涡','飘雪'],
                        ['操作说明   F1','软件说明','视频介绍','-','注册...','-','关于']]                   # 第二级菜单  其中的 '-' 为划一条分隔线 add_separator
        self.subcommand=[['新建.showTK("新建")','修改.showTK()','-','打开文件()','保存文件()','屏幕截图()','-','从内置函数挑选()','-','退出系统()'],
                        ['画圆()','椭圆','抛物线','正弦曲线','正切曲线','两个方程求共解','-','锯齿波','方波','-','笑脸','美之心(纹)','五星红旗'],
                        ['阿基米德螺线','伯努利双扭线','笛卡尔心形线','-','回旋','绳结','花心','-','模拟表盘','扫描','绚丽图形','头结','蝴蝶曲线'],
                        ['改变菜单主题()','-','选择坐标系()','-','绘图方式()','画笔颜色设置()','-','背景图片集.切换()','图例.图例设置()','-','调整屏幕大小()','-','调整相关变量()'],
                        ['天体运行模拟系统()','导弹轨迹()','宇宙大爆炸()','-','三体()','魂斗罗()','-','屏幕爬虫()','彩虹漩涡()','飘雪()'],
                        ['操作说明(1)','软件说明()','播放视频()','-','注册()','-','关于()']]                # 点选后需要执行的命令

程序中主题的实现,实际就是一些颜色的定义,本演示只简单用了两种主题:

    def menu_colors(self):
        if 背景颜色==(255,255,255):
            self.fgcolor=(232,232,232)              # 前景色
            self.bgcolor=(33,34,35)                 # 背景色
            self.activefgcolor=(68,68,68)           # 鼠标在其上时字体显示的颜色
            self.activebgcolor=(132,188,230)        # 鼠标在其上时背景显示的颜色
            self.submenu_bg_color=(70,70,70)        # 二级菜单的背景颜色
            self.submenu_border_color=(2,2,2)       # 二级菜单边沿处颜色
        else:
            self.fgcolor=(68,68,68)                 # 字体颜色
            self.bgcolor=(251,252,253)              # 主菜单背景颜色
            self.activefgcolor=(68,68,68)           # 鼠标在其上时字体颜色
            self.activebgcolor=(168,210,253)        # 鼠标在其上时背景颜色
            self.submenu_bg_color=(251,252,253)     # 二级菜单的背景颜色
            self.submenu_border_color=(220,221,222) # 二级菜单边沿处颜色

鼠标和键盘,用 pygame.event.get() 获取相关事件来实现。当鼠标移动到屏幕上端达到菜单高度范围内时,执行命令:mouse_over_menu() ,激活菜单。

            elif  event.type==MOUSEMOTION : # 鼠标移动事件 pos, rel, buttons  三个参数说明:当前鼠标坐标,相对移动距离,按键 ,数据格式分别为:(1619, 7) (0, 0, 0) (3, -6)
                if 0<=event.pos[1]<=菜单栏高度 :  # 菜单位置
                    菜单.mouse_over_menu(posx=event.pos[0])
                    显示鼠标处数值(event.pos,'提示:请您在菜单栏中选择')
                elif 菜单.get_sub_is_active(): # 有二级菜单激活状态
                    subrect=菜单.get_subrect()
                    if (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]):
                        菜单.mouse_over_submenu(posy=event.pos[1])
                    显示鼠标处数值(event.pos,'提示:可以用鼠标或键盘上下左右键选择相应菜单')
    def mouse_over_menu(self,posx): # 鼠标经过
        self.pos_to_i(posx)
        if self.i is not None:
            self.menu_chose()
        else:
            self.menu_root()
        if self.sub_is_active :     # 如果二级菜单已经打开
            self.submenu()

如果有点击鼠标事件发生,则执行:menu_click(),如果在打开的菜单上点击,执行 submenu_click()

            elif  event.type==MOUSEBUTTONDOWN :     # 有鼠标被按下的事件发生
                if event.button==1 :                # 鼠标左键按下
                    if 0<=event.pos[1]<=菜单栏高度 :# 菜单位置
                        菜单.menu_click(posx=event.pos[0],posy=event.pos[1])
                    elif 菜单.get_sub_is_active(): # 有二级菜单激活状态
                        subrect=菜单.get_subrect()
                        if (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]):
                            菜单.submenu_click(posx=event.pos[0],posy=event.pos[1])
                        else:
                            菜单.lost_focus()
                    else:
                        菜单.lost_focus()

定义键盘快捷键操作:按alt+定义按键,执行 menu_alt()

                elif mods & pygame.KMOD_ALT and event.key<256:
                    if chr(event.key).upper() in 'FRPSQH': # 主菜单项
                        菜单.menu_alt(key=event.key)

菜单打开状态下,鼠标和键盘上、下、左、右、回车键均可选择命令,用 menu_key() 选择命令,


        if key==K_ESCAPE:
            菜单.lost_focus()
        elif key==K_LEFT:
            if self.i>0:
                self.i -= 1
            self.open_submenu(self.i,0)
        elif key==K_RIGHT:
            if self.i<len(self.menus)-1:
                self.i += 1
            self.open_submenu(self.i,0)
        elif key==K_DOWN:
            if self.j is None:
                self.j=0
            elif self.j<=len(self.submenus[self.i])-1:
                self.j += 1
                if self.j<len(self.submenus[self.i])-1 and self.submenus[self.i][self.j][0]=='-':
                    self.j += 1
            if self.j>=len(self.submenus[self.i]):
                self.j=0
            self.open_submenu(self.i,self.j)
        elif key==K_UP:
            if self.j is None:
                self.j=len(self.submenus[self.i])-1
            if self.j>=0:
                self.j -= 1
                if self.submenus[self.i][self.j][0]=='-':
                    self.j -= 1
            if self.j<0:
                self.j=len(self.submenus[self.i])-1
            self.open_submenu(self.i,self.j)
        elif key==K_RETURN or key==K_KP_ENTER:
            self.submenu_click()
        elif key<256 and chr(key).upper() in 'FRPSQH': # 主菜单项
            self.menu_alt(key)

然后,根据点选内容,执行相应命令。相应命令存在变量 self.subcommand 中,是str格式,可以用exec() 或 evel() 执行。

当鼠标或键盘焦点不在菜单上时,要执行 lost_focus() ,取消菜单激活状态。

程序中通过保存屏幕和显示保存的屏幕,实现菜单的快速切换,如果程序很小,可以直接刷新,这需要开发者另外来改写了。

程序中还有一些其它技巧,读取代码参考吧,好了,现在上完整代码:

# encoding:utf-8
# Copyright (C) 2021-2022 Liu Qinghao
__author__ = 'Liu Qinghao' # 作者
'''
# Tel: 13837335336
# 微信: yikehongxin6666
# Email: 11220200@QQ.com
'''
from sys import  exit
import pygame
from pygame.locals import *
from random import randint
from math import floor

class mymenu(object):                            # 在pygame中定义菜单,支持鼠标和键盘操作
    def __init__(self):
        self.menu_height=菜单栏高度
        self.sub_top_height=12                   # 子菜单第一行上边保留空间
        self.default_fontsize=16                 # 菜单初始字体大小
        self.mainfontsize=self.default_fontsize  # 主菜单字体大小
        self.subfontsize =self.default_fontsize  # 子菜单字体大小 当菜单高度大于窗口高度时,字体会自动缩小,以适应窗口
        self.menus    =[ '文件(F)','直角坐标函数展示(R)','极坐标函数展示(P)','功能(S)','趣味(Q)','帮助(H)'] # 第一级菜单 即主菜单
        self.submenus =[['新建...       Ctrl+N','修改...','-','打开文件...   Ctrl+O','保存到文件... Ctrl+S','保存图片... ','-','打开内置函数...','-','退出          Alt+F4'],
                        ['圆','椭圆','抛物线','正弦曲线','正切曲线','两个方程求共解','-','锯齿波','方波','-','笑脸','美之心(纹)','五星红旗'],
                        ['阿基米德螺线','伯努利双扭线','笛卡尔心形线','-','回旋','绳结','花心','-','模拟表盘','扫描','绚丽图形','头结','蝴蝶曲线'],
                        ['改变菜单主题     空格','-','坐标系...          F9','-','绘图方式...      1234','画笔颜色...       F10','-','切换背景图片       F6','图例设置...','-','屏幕大小...       F12','-','更多设置...'],
                        ['天体运行模拟系统','导弹模拟系统','宇宙大爆炸模拟系统','-','三体游戏','魂斗罗游戏','-','屏幕爬虫','彩虹漩涡','飘雪'],
                        ['操作说明   F1','软件说明','视频介绍','-','注册...','-','关于']]                   # 第二级菜单  其中的 '-' 为划一条分隔线 add_separator
        self.subcommand=[['新建.showTK("新建")','修改.showTK()','-','打开文件()','保存文件()','屏幕截图()','-','从内置函数挑选()','-','退出系统()'],
                        ['画圆()','椭圆','抛物线','正弦曲线','正切曲线','两个方程求共解','-','锯齿波','方波','-','笑脸','美之心(纹)','五星红旗'],
                        ['阿基米德螺线','伯努利双扭线','笛卡尔心形线','-','回旋','绳结','花心','-','模拟表盘','扫描','绚丽图形','头结','蝴蝶曲线'],
                        ['改变菜单主题()','-','选择坐标系()','-','绘图方式()','画笔颜色设置()','-','背景图片集.切换()','图例.图例设置()','-','调整屏幕大小()','-','调整相关变量()'],
                        ['天体运行模拟系统()','导弹轨迹()','宇宙大爆炸()','-','三体()','魂斗罗()','-','屏幕爬虫()','彩虹漩涡()','飘雪()'],
                        ['操作说明(1)','软件说明()','播放视频()','-','注册()','-','关于()']]                # 点选后需要执行的命令
        self.subsize  = []
        for i in range(len(self.submenus)):      # 二级菜单大小 w h ,根据内容自动算出其宽和高度
            self.subsize.append([len(max(self.submenus[i],key=lencc).encode('GBK'))*self.subfontsize/2+30 ,
                                len(self.submenus[i])*self.subfontsize*2 +self.sub_top_height])
        topline=int((self.menu_height-self.subfontsize)/2) # 子菜单第一行上面留空
        self.xy= [(12,topline),(84,topline),(252,topline),(403,topline),(475,topline),(547,topline),(622,topline)]  # 定义每个菜单的起始位置,数量应比self.menus多一个数,最后一个数是最后一个菜单的最后坐标
        # 以上几个数据必须完全对应才行,否则会出错!!! 注意数据排列方式!!!

        self.i = None     # 记忆一级菜单位置 (一级菜单也叫主菜单)
        self.j = None     # 记忆二级菜单位置
        self.subrect=None # 二级菜单感应鼠标范围
        self.sub_is_active=False # 二级菜单是否已经打开
        self.menu_root()

    def menu_colors(self):
        if 背景颜色==(255,255,255):
            self.fgcolor=(232,232,232)              # 前景色
            self.bgcolor=(33,34,35)                 # 背景色
            self.activefgcolor=(68,68,68)           # 鼠标在其上时字体显示的颜色
            self.activebgcolor=(132,188,230)        # 鼠标在其上时背景显示的颜色
            self.submenu_bg_color=(70,70,70)        # 二级菜单的背景颜色
            self.submenu_border_color=(2,2,2)       # 二级菜单边沿处颜色
        else:
            self.fgcolor=(68,68,68)                 # 字体颜色
            self.bgcolor=(251,252,253)              # 主菜单背景颜色
            self.activefgcolor=(68,68,68)           # 鼠标在其上时字体颜色
            self.activebgcolor=(168,210,253)        # 鼠标在其上时背景颜色
            self.submenu_bg_color=(251,252,253)     # 二级菜单的背景颜色
            self.submenu_border_color=(220,221,222) # 二级菜单边沿处颜色

    def menu_root(self):            # 菜单的基础
        self.menu_colors()
        pygame.draw.rect(screen,self.bgcolor,(0,0,screen_w,self.menu_height))
        for i in range(len(self.menus)):
            str_to_screen(' '+self.menus[i]+' ',self.xy[i][0],self.xy[i][1],self.mainfontsize,self.fgcolor,bgcolor=self.bgcolor,bold=True)

    def mouse_over_menu(self,posx): # 鼠标经过
        self.pos_to_i(posx)
        if self.i is not None:
            self.menu_chose()
        else:
            self.menu_root()
        if self.sub_is_active :     # 如果二级菜单已经打开
            self.submenu()

    def menu_chose(self):           # 画主菜单被选择部分
        pygame.draw.rect(screen,self.activebgcolor,(self.xy[self.i][0],0,self.xy[self.i+1][0]-self.xy[self.i][0],self.menu_height))
        str_to_screen(' '+self.menus[self.i]+' ',self.xy[self.i][0],self.xy[self.i][1],self.mainfontsize,self.activefgcolor,bgcolor=self.activebgcolor)

    def submenu_chose(self):
        pygame.draw.rect(screen,self.activebgcolor,(self.子菜单坐标x[self.j]-10,self.子菜单坐标y[self.j]-self.subfontsize/2,self.subsize[self.i][0]*(self.subfontsize/self.default_fontsize)+10,self.subfontsize*2))
        str_to_screen(' '+self.submenus[self.i][self.j]+' ',self.子菜单坐标x[self.j],self.子菜单坐标y[self.j],self.subfontsize,self.activefgcolor,bgcolor=self.activebgcolor)

    def mouse_over_submenu(self,posy): # 鼠标经过二级菜单
        self.submenu()
        self.pos_to_j(posy)
        if self.j is not None:
            self.submenu_chose()

    def pos_to_j(self,posy): # 确定鼠标在哪个二级菜单上
        j2=floor((posy-12-self.menu_height)/self.subfontsize)  # 向下取整
        j=floor(j2/2)
        if j2%2==0 and (self.i is not None) and self.submenus[self.i][j][0] != '-':
            self.j=j
        else:
            self.j=None

    def pos_to_i(self,posx): # 确定鼠标在哪个主菜单上
        self.i=None
        self.j=None
        for i in range(len(self.menus)):
            if self.xy[i][0] <= posx < self.xy[i+1][0]:
                self.menu_root()
                self.i=i
                break

    def save_screen(self):
        self.sub = screen.subsurface((0,self.menu_height,screen_w,screen_h-self.menu_height-底部状态栏高度)).copy() # 保存屏幕

    def recovery_screen(self):
        self.j=None
        screen.blit(self.sub, (0,self.menu_height)) # 从保存的屏幕中恢复屏幕

    def lost_focus(self):           # 失去焦点时
        self.recovery_screen()
        self.sub_is_active=False
        self.i=None
        self.menu_root()
        # pygame.display.update()

    def menu_click(self,posx,posy): # 主菜单点击事件
        self.mouse_over_menu(posx)
        self.submenu()

    def menu_alt(self,key): # 键盘 alt+菜单字母 被按下
        # FRPSQH  self.menus=[ '文件(F)','直角坐标系函数(R)','极坐标函数(P)','设置(S)','趣味(Q)','帮助(H)']
        for i in range(len(self.menus)):
            if chr(key).upper() in self.menus[i]:
                self.open_submenu(i,0)
                return

    def menu_key(self,key): # 在菜单被激活时处理传过来的按键
        if key==K_ESCAPE:
            菜单.lost_focus()
        elif key==K_LEFT:
            if self.i>0:
                self.i -= 1
            self.open_submenu(self.i,0)
        elif key==K_RIGHT:
            if self.i<len(self.menus)-1:
                self.i += 1
            self.open_submenu(self.i,0)
        elif key==K_DOWN:
            if self.j is None:
                self.j=0
            elif self.j<=len(self.submenus[self.i])-1:
                self.j += 1
                if self.j<len(self.submenus[self.i])-1 and self.submenus[self.i][self.j][0]=='-':
                    self.j += 1
            if self.j>=len(self.submenus[self.i]):
                self.j=0
            self.open_submenu(self.i,self.j)
        elif key==K_UP:
            if self.j is None:
                self.j=len(self.submenus[self.i])-1
            if self.j>=0:
                self.j -= 1
                if self.submenus[self.i][self.j][0]=='-':
                    self.j -= 1
            if self.j<0:
                self.j=len(self.submenus[self.i])-1
            self.open_submenu(self.i,self.j)
        elif key==K_RETURN or key==K_KP_ENTER:
            self.submenu_click()
        elif key<256 and chr(key).upper() in 'FRPSQH': # 主菜单项
            self.menu_alt(key)

    def open_submenu(self,i,j): # 把焦点定位到一个子菜单上
        self.i=i
        self.menu_root()
        self.menu_chose()
        self.submenu()
        self.j=j
        self.submenu_chose()

    def submenu_click(self,posx=0,posy=0): # 二级菜单点击事件 后两个参数感觉没用,经长时间运行后如果没有问题可以删除
        if self.i is not None and self.j is not None:
            command=self.subcommand[self.i][self.j]
            self.lost_focus()
            pygame.display.update()

            print(f'您选择要执行的命令是 {command} ,你可以用 eval() 或 exec() 的方式执行该命令')
            if '退出' in command or '主题' in command or '画圆' in command:
                exec(command)

    def submenu(self): # 创建二级菜单
        self.recovery_screen()
        # pr('self.i=',self.i)
        if self.i is None:
            return
        self.sub_is_active=True # 二级菜单是否已经打开
        self.子菜单坐标x=[] # self.xy[self.i][0]+10
        self.子菜单坐标y=[] # self.menu_height+12
        起点x=self.xy[self.i][0]+10
        起点y=self.menu_height+12

        self.subfontsize=self.default_fontsize
        self.subrect=[起点x-10,起点y,self.subsize[self.i][0]*(self.subfontsize/self.default_fontsize)+10]
        子菜单高度=len(self.submenus[self.i])*self.subfontsize*2 +12
        pygame.draw.rect(screen,self.submenu_bg_color    ,(self.xy[self.i][0],self.menu_height,self.subsize[self.i][0]*(self.subfontsize/self.default_fontsize)+10,子菜单高度)) # 二级菜单 的 背景
        pygame.draw.rect(screen,self.submenu_border_color,(self.xy[self.i][0],self.menu_height,self.subsize[self.i][0]*(self.subfontsize/self.default_fontsize)+10,子菜单高度),1) # 二级菜单 的 边框
        for i in range(len(self.submenus[self.i])):
            self.子菜单坐标x.append(起点x)
            self.子菜单坐标y.append(起点y)
            if self.submenus[self.i][i][0]=='-':
                pygame.draw.rect(screen,self.fgcolor,(self.子菜单坐标x[i],int(self.子菜单坐标y[i]+self.subfontsize/2),self.subsize[self.i][0]*(self.subfontsize/self.default_fontsize)+10-20,1))
                起点y += self.subfontsize *2
            else:
                str_to_screen(' '+self.submenus[self.i][i]+' ',self.子菜单坐标x[i],self.子菜单坐标y[i],self.subfontsize,self.fgcolor)
                起点y += self.subfontsize *2
        self.subrect.append(起点y-self.subrect[1])

    def get_sub_is_active(self): # 获取二级菜单是否已经打开的状态
        return self.sub_is_active

    def get_subrect(self):       # 获取菜单感应鼠标坐标范围 (x,y,w,h)
        return self.subrect

    def get_i(self):             # 获取当前主菜单位置
        return self.i

    def get_j(self):             # 获取当前二级菜单位置
        return self.j

def str_to_screen(string,x,y,fontsize,color,bgcolor=None,bold=False):      # 参数说明:(显示的内容,左上角坐标x y,字体大小,字颜色)  显示的内容:可以接收数字(int ,float)和字符类型(str)
    if isinstance(string,(int,float)):  # 判断数据类型
        string=str(string)              # 把数据转换成字符串
    cur_font = pygame.font.SysFont(字体, int(fontsize))
    textSurf = cur_font.render(string, True,  color,bgcolor)
    cur_font.set_bold(bold)
    TextRect = (x,y)
    screen.blit(textSurf,TextRect)

def 显示鼠标处数值(屏幕坐标,鼠标处数值):
    pygame.draw.rect(screen,(0,0,255), (0,screen_h-底部状态栏高度,screen_w,底部状态栏高度),width=0) # 清空状态栏
    if 屏幕坐标==(-1,-1):
        pygame.draw.rect(screen,(0,0,255), (0,screen_h-底部状态栏高度,screen_w,底部状态栏高度),width=0) # 清空状态栏
        str_to_screen(鼠标处数值,3,screen_h-底部状态栏高度+3,14,(255,255,255),(0,0,255))
        return

    str_to_screen(f' 屏幕坐标:{int(屏幕坐标[0])},{int(屏幕坐标[1])}',\
        0,screen_h-底部状态栏高度+3,14,(255,255,255),(0,0,255))
    str_to_screen(f'{鼠标处数值}',270,screen_h-底部状态栏高度+3,14,(255,255,255),(0,0,255))

def 改变窗口大小(screen_w_h=(700,600),状态=RESIZABLE):
    global screen,screen_w,screen_h
    screen_w,screen_h=screen_w_h
    if screen_w <700:  # 限止最小宽度
        screen_w = 700
    if screen_h <600:  # 限止最小高度
        screen_h = 600
    screen = pygame.display.set_mode((screen_w,screen_h),状态)
    pygame.draw.rect(screen,(0,0,255),(0,screen_h-底部状态栏高度,screen_w,底部状态栏高度)) # 绘制底部状态栏背景

def lencc(string): # 返回真实占用字节数(注:1个汉字占用2个字节)
    return len(string.encode('GBK'))

def 改变菜单主题():
    global 背景颜色
    if 背景颜色 == (255,255,255):
        背景颜色 = (0,0,0)
    else:
        背景颜色 = (255,255,255)
    清空背景()
    菜单.lost_focus()

def 清空背景():
    screen.fill(背景颜色)

def 画圆(): # 随机画几个圆,用于演示
    清空背景()
    for i in range(20):
        xy = (randint(0,screen_w),randint(0,screen_h))
        color = (randint(0,255),randint(0,255),randint(0,255))
        r  = randint(10,300)
        w  = randint(0,5)
        pygame.draw.circle(screen,color,xy,r,w)
    菜单.menu_root()
    菜单.save_screen()

def 退出系统():
    exit()

# 主程序 ===========================
def main(NoWelcom=False):
    global screen_w,screen_h,底部状态栏高度 , 菜单栏高度 , screen,菜单 ,字体,背景颜色
    pygame.init()
    pygame.display.set_caption('Pygame菜单演示')
    字体='simsun'
    底部状态栏高度 = 22
    菜单栏高度 = 22
    背景颜色 = (255,255,255)
    改变窗口大小((1000, 800),RESIZABLE)
    菜单=mymenu() # 初始化顶部菜单
    菜单.save_screen()
    while 1:
        for event in pygame.event.get():
            mods=pygame.key.get_mods()  # 返回组合键的掩码
            pygame.event.clear()  # 从队列中清除所有的事件
            if event.type == pygame.QUIT:
                退出系统()
            elif event.type==KEYDOWN : # 有键盘输入的事件发生
                if 菜单.get_sub_is_active(): # 有二级菜单激活状态
                    菜单.menu_key(event.key)
                elif event.key==K_ESCAPE:
                    退出系统()
                elif event.key==K_SPACE:
                    改变菜单主题()
                elif mods & pygame.KMOD_ALT and event.key<256:
                    if chr(event.key).upper() in 'FRPSQH': # 主菜单项
                        菜单.menu_alt(key=event.key)
            elif  event.type==MOUSEBUTTONDOWN :     # 有鼠标被按下的事件发生
                if event.button==1 :                # 鼠标左键按下
                    if 0<=event.pos[1]<=菜单栏高度 :# 菜单位置
                        菜单.menu_click(posx=event.pos[0],posy=event.pos[1])
                    elif 菜单.get_sub_is_active(): # 有二级菜单激活状态
                        subrect=菜单.get_subrect()
                        if (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]):
                            菜单.submenu_click(posx=event.pos[0],posy=event.pos[1])
                        else:
                            菜单.lost_focus()
                    else:
                        菜单.lost_focus()

                elif event.button==2 :            # 鼠标中键按下
                    if 菜单.get_sub_is_active():  # 有二级菜单激活状态
                        菜单.lost_focus()

                elif event.button==3 :           # 鼠标右键按下
                    if 菜单.get_sub_is_active(): # 有二级菜单激活状态
                        if (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]): # 鼠标在二级菜单内点击
                            菜单.submenu_click(posx=event.pos[0],posy=event.pos[1])
                        else:                    # 鼠标在二级菜单以外点击
                            菜单.lost_focus()

                elif event.button==4 :           # 鼠标中键向前滚轮
                    subrect=菜单.get_subrect()
                    if 菜单.get_sub_is_active() and (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]): # 有二级菜单激活状态
                        ...                      # 在二级菜单内滚轮
                    else:
                        菜单.lost_focus()

                elif event.button==5 :        # 鼠标中键向后滚轮
                    subrect=菜单.get_subrect()
                    if 菜单.get_sub_is_active() and (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]): # 有二级菜单激活状态
                        ...                   # 在二级菜单内滚轮
                    else:
                        菜单.lost_focus()

            elif  event.type==VIDEORESIZE : # 窗口缩放事件
                改变窗口大小(event.size)

            elif  event.type==MOUSEMOTION : # 鼠标移动事件 pos, rel, buttons  三个参数说明:当前鼠标坐标,相对移动距离,按键 ,数据格式分别为:(1619, 7) (0, 0, 0) (3, -6)
                if 0<=event.pos[1]<=菜单栏高度 :  # 菜单位置
                    菜单.mouse_over_menu(posx=event.pos[0])
                    显示鼠标处数值(event.pos,'提示:请您在菜单栏中选择')
                elif 菜单.get_sub_is_active(): # 有二级菜单激活状态
                    subrect=菜单.get_subrect()
                    if (subrect[0] < event.pos[0] < subrect[0] +subrect[2])  and (subrect[1] < event.pos[1] < subrect[1] +subrect[3]):
                        菜单.mouse_over_submenu(posy=event.pos[1])
                    显示鼠标处数值(event.pos,'提示:可以用鼠标或键盘上下左右键选择相应菜单')
                else:
                    菜单.lost_focus()
                    显示鼠标处数值(event.pos,'')

            elif  event.type==WINDOWLEAVE :     # 鼠标离开窗口事件
                菜单.lost_focus()

        pygame.display.update()

if __name__ == '__main__':
    main()

复制粘贴可以直接执行哦 :)

使用pygame实现开始菜单可以通过创建一个游戏循环,在循环绘制开始菜单的图像和按钮,然后监听按钮的事件并响应。以下是一个简单的实现方式: ```python import pygame pygame.init() # 设置窗口大小和标题 size = (600, 400) screen = pygame.display.set_mode(size) pygame.display.set_caption("Start Menu") # 加载图片和字体 background_image = pygame.image.load("start_menu_background.jpg").convert() start_button_image = pygame.image.load("start_button.png").convert_alpha() font = pygame.font.SysFont('Arial', 30) # 设置按钮的位置和大小 button_width = 200 button_height = 80 button_x = (size - button_width) // 2 button_y = (size - button_height) // 2 # 设置文字的位置和颜色 text = font.render("Start", True, (255, 255, 255)) text_x = button_x + (button_width - text.get_width()) // 2 text_y = button_y + (button_height - text.get_height()) // 2 # 游戏循环 running = True while running: # 监听事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.MOUSEBUTTONDOWN: # 判断鼠标是否点击了按钮 if button_x < event.pos < button_x + button_width and \ button_y < event.pos < button_y + button_height: print("Start Button Clicked!") # 绘制背景图片、按钮和文字 screen.blit(background_image, (0, 0)) screen.blit(start_button_image, (button_x, button_y)) screen.blit(text, (text_x, text_y)) # 更新屏幕 pygame.display.update() # 退出pygame pygame.quit() ``` 在这个实现,我们首先加载了开始菜单的背景图片、按钮图片和字体。然后设置了按钮的位置和大小以及文字的位置和颜色。接着在游戏循环,我们监听了鼠标事件,并判断鼠标是否点击了按钮。最后,我们绘制了背景图片、按钮和文字,并更新了屏幕。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘庆豪2007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值