代码之家  ›  专栏  ›  技术社区  ›  Owen683

向嵌入式Pygame分配按键

  •  8
  • Owen683  · 技术社区  · 7 年前

    我的代码在下面(对不起,太长了)

    import pygame, os, _tkinter, sys
    try:
        import Tkinter as tk
        BOTH,LEFT,RIGHT,TOP,BOTTOM,X,Y = tk.BOTH,tk.LEFT,tk.RIGHT,tk.TOP,tk.BOTTOM,tk.X,tk.Y
        two = True
    except ImportError:
        import tkinter as tk
        from tkinter.constants import *
        two = False
    from pygame.locals import *
    
    class PygameWindow(tk.Frame):
        """ Object for creating a pygame window embedded within a tkinter window.
    
            Please note: Because pygame only supports a single window, if more than one
            instance of this class is created then updating the screen on one will update
            all of the windows.
        """
        def __init__(self, pygame_size, pygame_side, master=None, **kwargs):
            """
                Parameters:
                pygame_size - tuple - The initial size of the pygame screen
                pygame_side - string - A direction to pack the pygame window to
                master - The window's master (often a tk.Tk() instance
                pygame_minsize - tuple - The minimum size of the pygame window.
                    If none is specified no restrictions are placed
                pygame_maxsize - tuple - The maximum size of the pygame window.
                    If none is specified no restrictions are placed.
                    Note: This includes the pygame screen even when fullscreen.
                tkwin - string - A direction to pack a tkinter frame to designed to be
                    used to contain widgets to interact with the pygame window.
                    If none is specified the frame is not included.
                fullscreen - boolean - Whether fullscreen should be allowed
                menu - boolean - Whether a menu bar should be included.
                    the menu bar contains a File menu with quit option and an Options
                    menu with fullscreen option (If enabled)
            """
            # I have decided to use a global variable here because pygame only supports a single screen,
            # this should limit confusion if multiple instances of the class are created because each
            # instance will always contain the same screen. A global variable should hopefully make this
            # clearer than screen being a seperate attribute for each instance
            global screen
            self.master = master
            self.fullscreen = tk.BooleanVar(value=False)
            if two:
                tk.Frame.__init__(self,master)
            else:
                super().__init__(self,master)
            self.pack(fill=BOTH,expand=1)
    
            if 'pygame_minsize' in kwargs:
                w,h = kwargs['pygame_minsize']
                master.minsize(w,h)
                del kwargs['pygame_minsize']
    
            w,h = pygame_size
            self.embed = tk.Frame(self, width = w, height = h)
            self.embed.pack(side=pygame_side,fill=BOTH,expand=1)
    
            if 'tkwin' in kwargs:
                if kwargs['tkwin'] != None:
                    self.tk_frame = tk.Frame(self,bg='purple')
                    if kwargs['tkwin'] in [TOP,BOTTOM]:
                        self.tk_frame.pack(side=kwargs['tkwin'],fill=X)
                    elif kwargs['tkwin'] in [LEFT,RIGHT]:
                        self.tk_frame.pack(side=kwargs['tkwin'],fill=Y)
                    else:
                        raise ValueError('Invalid value for tkwin: "%r"' %kwargs['tkwin'])
                del kwargs['tkwin']
    
            if 'fullscreen' in kwargs:
                if kwargs['fullscreen']:
                    self.fs_okay = True
                else:
                    self.fs_okay = False
            else:
                self.fs_okay = False
    
            os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
            if sys.platform == "win32":
                os.environ['SDL_VIDEODRIVER'] = 'windib'
            pygame.display.init()
    
            if 'pygame_maxsize' in kwargs:
                w,h = kwargs['pygame_maxsize']
                self.pygame_maxsize = (w,h)
                screen = pygame.display.set_mode((w,h),RESIZABLE)
                del kwargs['pygame_maxsize']
            else:
                screen = pygame.display.set_mode((0,0),RESIZABLE)
                self.pygame_maxsize = (0,0)
            screen.fill((255,255,255))
    
            if 'menu' in kwargs:
                if kwargs['menu']:
                    self.menubar = tk.Menu(self.master)
                    self.master.config(menu=self.menubar)
    
                    self.filemenu = tk.Menu(self.menubar,tearoff=0)
                    self.filemenu.add_command(label='Quit',command=self.close,accelerator='Ctrl+Q')
                    self.menubar.add_cascade(label='File',menu=self.filemenu)
    
                    self.optionmenu = tk.Menu(self.menubar,tearoff=0)
                    if self.fs_okay:
                        self.optionmenu.add_checkbutton(label='Fullscreen',command=self.updatefs,variable=self.fullscreen,accelerator='F11')
                    self.menubar.add_cascade(label='Options',menu=self.optionmenu)
    
        def update(self):
            """ Update the both the contents of the pygame screen and
                the tkinter window. This should be called every frame.
            """
            pressed = pygame.key.get_pressed()
            if self.fullscreen.get():
                if pressed[K_ESCAPE] or pressed[K_F11] or not pygame.display.get_active():
                    self.togglefs()
            else:
                if pressed[K_q] and (pressed[K_LCTRL] or pressed[K_RCTRL]):
                    self.close()
                for event in pygame.event.get(KEYDOWN):
                    if event.key == K_F11:
                        self.togglefs()
                    pygame.event.post(event)
            pygame.event.pump()
            pygame.display.flip()
            self.master.update()
    
        def close(self,*args):
            """ Closes the open window."""
            self.master.destroy()
    
        def togglefs(self,*args):
            """Toggles the self.fullscreen variable and then calls
                the updatefs function.
            """
            self.fullscreen.set(not self.fullscreen.get())
            self.updatefs()
    
        def updatefs(self):
            """Updates whether the window is fullscreen mode or not
                dependent on the value of the fullscreen attribute.
            """
            if not self.fs_okay:
                self.fullscreen.set(False)
            global screen
            tmp = screen.convert()
            cursor = pygame.mouse.get_cursor()
            flags = screen.get_flags()
            bits = screen.get_bitsize()
    
            if self.fullscreen.get():
                pygame.display.quit()
                del os.environ['SDL_WINDOWID']
                if sys.platform == "win32":
                    del os.environ['SDL_VIDEODRIVER']
                pygame.display.init()
                screen = pygame.display.set_mode(self.pygame_maxsize,FULLSCREEN|(flags&~RESIZABLE),bits)
            else:
                pygame.display.quit()
                os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
                if sys.platform == "win32":
                    os.environ['SDL_VIDEODRIVER'] = 'windib'
                pygame.display.init()
                screen = pygame.display.set_mode(self.pygame_maxsize,RESIZABLE|(flags&~FULLSCREEN),bits)
            screen.blit(tmp,(0,0))
            pygame.mouse.set_cursor(*cursor)
    
    
    class TestWindow(PygameWindow):
        def __init__(self, pygame_size, pygame_side, master=None, **kwargs):
            if two:
                PygameWindow.__init__(self,pygame_size, pygame_side, master=master, **kwargs)
            else:
                super().__init__(self,pygame_size, pygame_side, master=master, **kwargs)
            self.drawn = False
            self.button1 = tk.Button(self.tk_frame,text = 'Draw',  command=self.draw)
            self.button1.pack(side=LEFT)
            screen.fill((255,255,255))
            pygame.display.flip()
    
        def draw(self):
            if not self.drawn:
                pygame.draw.circle(screen, (0,255,175), (250,250), 125)
            else:
                screen.fill((255,255,255))
            self.drawn = not self.drawn
    
    if __name__ == '__main__':
        root = tk.Tk()
        window = TestWindow((500,500),LEFT,root,pygame_minsize=(500,500),tkwin=LEFT,menu=True,fullscreen=True)
    
        while True:
            try:
                window.update()
            except _tkinter.TclError:
                break
    quit()
    
    1 回复  |  直到 7 年前
        1
  •  5
  •   Right leg A.s. Bhullar    7 年前

    其思想是将要调度的密钥绑定到 dispatch_event_to_pygame 函数,该函数将创建相应的 pygame.event.Event pygame.event.post 作用

    首先,我定义了一个字典,它建立了我想从tkinter发送到pygame的键与pygame中相应符号之间的对应关系:

    tkinter_to_pygame = {
        'Down':     pygame.K_DOWN,
        'Up':       pygame.K_UP,
        'Left':     pygame.K_LEFT,
        'Right':    pygame.K_RIGHT}
    

    dispatch_event_to_pygame 函数接受tkinter事件,创建相应的pygame事件并发布:

    def dispatch_event_to_pygame(tkEvent):
        if tkEvent.keysym in tkinter_to_pygame:
            pgEvent = pygame.event.Event(pygame.KEYDOWN,
                                         {'key': tkinter_to_pygame[tkEvent.keysym]})
            pygame.event.post(pgEvent)
    

    for key in tkinter_to_pygame:
        root.bind("<{}>".format(key), dispatch_event_to_pygame)
    

    密钥名称参考: