2017-12-04 15 views
1

Ich habe Schaltflächen in Pygame für Klickereignisse gemacht. Aber es gibt ein Problem, wenn ich die Maustaste klicke, wenn ich die Maus zwischen den Schaltflächengrenzen bewege, klicke auf das Ereignis wiederholt sich. Ich möchte nur einen einzigen Klick, bis ich die Maustaste loslassen. Wie kann ich es schaffen?Pygame-Taste Einzelklick

import pygame,time 
pygame.init() 
x,y = (200,300) 
pencere = pygame.display.set_mode((x,y)) 
pygame.display.set_caption("Click") 

white = (255,255,255) 
black = (0,0,0) 
black2 = (30,30,30) 

class Counter: 
    count = 0 
    def click(self): 
     self.count += 1 

number = Counter() 
def text_objects(text, font, color): 
    textSurface = font.render(text, True, color) 
    return textSurface, textSurface.get_rect() 

def button(msg,x,y,w,h,c,ic,action=None): 
    mouse = pygame.mouse.get_pos() 
    click = pygame.mouse.get_pressed() 
    pygame.draw.rect(pencere, c,(x,y,w,h)) 

    smallText = pygame.font.Font("freesansbold.ttf",20) 
    textSurf, textRect = text_objects(msg, smallText, white) 
    textRect.center = ((x+(w/2)), (y+(h/2))) 
    pencere.blit(textSurf, textRect) 

    if x+w > mouse[0] > x and y+h > mouse[1] > y: 
     pygame.draw.rect(pencere, ic,(x,y,w,h)) 
     if click[0] == 1 != None: 
      action() 
     smallText = pygame.font.Font("freesansbold.ttf",20) 
     textSurf, textRect = text_objects(msg, smallText, white) 
     textRect.center = ((x+(w/2)), (y+(h/2))) 
     pencere.blit(textSurf, textRect) 
def loop(): 
    cikis = False 
    while not cikis: 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       cikis = True 
       pygame.quit() 
       quit() 
      pencere.fill(white) 
      smallText = pygame.font.Font("freesansbold.ttf",50) 
      textSurf, textRect = text_objects(str(number.count), smallText, black) 
      textRect.center = ((x/2)), (30) 
      pencere.blit(textSurf, textRect) 
      button("Click",0,100,200,200,black,black2,number.click) 
      pygame.display.update() 
loop() 
pygame.quit() 
quit() 
+0

Können Sie den von Ihnen verwendeten Code angeben? – serge1peshcoff

+0

müssen Sie zu Tastenvariable dh hinzufügen. 'clicked = True', um sich daran zu erinnern und es zu überprüfen, bevor Sie die der Schaltfläche zugewiesene Funktion ausführen. Und löschen Sie es, wenn Sie die Maustaste loslassen. – furas

+0

BTW: verwenden Sie 'event.type == MOUSEBUTTONDOWN' oder' pygame.mouse.get_pressed() '? Mit 'MOUSEBUTTONDOWN' sollte dieses Problem nicht auftreten, da ein Ereignis nur dann erzeugt wird, wenn die Maustaste den Zustand für 'nicht gedrückt' in 'gedrückt' ändert, aber nicht erzeugt wird, wenn die Maustaste gedrückt gehalten wird. 'get_pressed()' gibt 'True' zurück, wenn die Maustaste gedrückt gehalten wird. – furas

Antwort

1

Es gibt mehrere Dinge, die geändert werden sollen:

Die Zeichnung und Button-Code nicht in der Ereignisschleife sein sollte, aber in den äußeren while-Schleife. Sie rufen die Funktion button jedes Mal auf, wenn ein Ereignis auftritt (z. B. wenn sich die Maus bewegt).

Die button Funktion macht zu viel. Er erstellt und blendet Textoberflächen, zeichnet Reklikationen, prüft auf Kollisionen und ruft die Methode click auf.

Sie sollten pygame.mouse.get_pressed() nicht verwenden und stattdessen die MOUSEBUTTONDOWN Ereignisse in der Ereignisschleife behandeln. mouse.get_pressed prüft nur, ob eine Maustaste gedrückt ist und nicht, wenn ein einzelner Klick aufgetreten ist.

Ich zeige Ihnen einfach eine einfache Lösung ohne eine Funktion und mit einem Rect als die Taste hier. Ich handhabe die Kollision und aktualisiere die Nummer in der Ereignisschleife. Wenn Sie mehrere Schaltflächen erstellen möchten, würde ich vorschlagen, sie objektorientiert neu zu schreiben (ich kann Ihnen ein Beispiel zeigen, wenn Sie möchten).

import pygame 


pygame.init() 
width, height = (200,300) 
screen = pygame.display.set_mode((width, height)) 

WHITE = (255, 255, 255) 
BLACK = (0, 0, 0) 
GRAY = (30, 30, 30) 
FONT = pygame.font.Font("freesansbold.ttf", 50) 


def loop(): 
    clock = pygame.time.Clock() 
    number = 0 
    # The button is just a rect. 
    button = pygame.Rect(0, 100, 200, 200) 
    done = False 
    while not done: 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       done = True 
      # This block is executed once for each MOUSEBUTTONDOWN event. 
      elif event.type == pygame.MOUSEBUTTONDOWN: 
       # 1 is the left mouse button, 2 is middle, 3 is right. 
       if event.button == 1: 
        # `event.pos` is the mouse position. 
        if button.collidepoint(event.pos): 
         # Increment the number. 
         number += 1 

     screen.fill(WHITE) 
     pygame.draw.rect(screen, GRAY, button) 
     text_surf = FONT.render(str(number), True, BLACK) 
     # You can pass the center directly to the `get_rect` method. 
     text_rect = text_surf.get_rect(center=(width/2, 30)) 
     screen.blit(text_surf, text_rect) 
     pygame.display.update() 

     clock.tick(30) 


loop() 
pygame.quit() 

Nachtrag: Ich empfehle eine objektorientierte Lösung mit einer Button-Klasse, die eine Unterklasse von pygame.sprite.Sprite und kann zu Sprite Gruppen hinzugefügt werden. Sie können Ihre eigenen Bilder an die Klasse Button übergeben oder die Standardbilder verwenden. Sie müssen auch eine Callback-Funktion oder -Methode an jede Button-Instanz übergeben, die in der handle_event-Methode aufgerufen wird, um die spezifischen Attribute der Spielklasse zu aktualisieren (hier habe ich eine Methode, die einen Zähler erhöht und eine weitere, um das Spiel zu beenden).

import pygame as pg 


pg.init() 
screen = pg.display.set_mode((800, 600)) 
FONT = pg.font.SysFont('Comic Sans MS', 32) 
# Default button images/pygame.Surfaces. 
IMAGE_NORMAL = pg.Surface((100, 32)) 
IMAGE_NORMAL.fill(pg.Color('dodgerblue1')) 
IMAGE_HOVER = pg.Surface((100, 32)) 
IMAGE_HOVER.fill(pg.Color('lightskyblue')) 
IMAGE_DOWN = pg.Surface((100, 32)) 
IMAGE_DOWN.fill(pg.Color('aquamarine1')) 


# Button is a sprite subclass, that means it can be added to a sprite group. 
# You can draw and update all sprites in a group by 
# calling `group.update()` and `group.draw(screen)`. 
class Button(pg.sprite.Sprite): 

    def __init__(self, x, y, width, height, callback, 
       font=FONT, text='', text_color=(0, 0, 0), 
       image_normal=IMAGE_NORMAL, image_hover=IMAGE_HOVER, 
       image_down=IMAGE_DOWN): 
     super().__init__() 
     # Scale the images to the desired size (doesn't modify the originals). 
     self.image_normal = pg.transform.scale(image_normal, (width, height)) 
     self.image_hover = pg.transform.scale(image_hover, (width, height)) 
     self.image_down = pg.transform.scale(image_down, (width, height)) 

     self.image = self.image_normal # The currently active image. 
     self.rect = self.image.get_rect(topleft=(x, y)) 
     # To center the text rect. 
     image_center = self.image.get_rect().center 
     text_surf = font.render(text, True, text_color) 
     text_rect = text_surf.get_rect(center=image_center) 
     # Blit the text onto the images. 
     for image in (self.image_normal, self.image_hover, self.image_down): 
      image.blit(text_surf, text_rect) 

     # This function will be called when the button gets pressed. 
     self.callback = callback 
     self.button_down = False 

    def handle_event(self, event): 
     if event.type == pg.MOUSEBUTTONDOWN: 
      if self.rect.collidepoint(event.pos): 
       self.image = self.image_down 
       self.button_down = True 
     elif event.type == pg.MOUSEBUTTONUP: 
      # If the rect collides with the mouse pos. 
      if self.rect.collidepoint(event.pos) and self.button_down: 
       self.callback() # Call the function. 
       self.image = self.image_hover 
      self.button_down = False 
     elif event.type == pg.MOUSEMOTION: 
      collided = self.rect.collidepoint(event.pos) 
      if collided and not self.button_down: 
       self.image = self.image_hover 
      elif not collided: 
       self.image = self.image_normal 


class Game: 

    def __init__(self, screen): 
     self.done = False 
     self.clock = pg.time.Clock() 
     self.screen = screen 
     # Contains all sprites. Also put the button sprites into a 
     # separate group in your own game. 
     self.all_sprites = pg.sprite.Group() 
     self.number = 0 
     # Create the button instances. You can pass your own images here. 
     self.start_button = Button(
      320, 70, 170, 65, self.increment_number, 
      FONT, 'Increment', (255, 255, 255), 
      IMAGE_NORMAL, IMAGE_HOVER, IMAGE_DOWN) 
     # If you don't pass images, the default images will be used. 
     self.quit_button = Button(
      320, 240, 170, 65, self.quit_game, 
      FONT, 'Quit', (255, 255, 255)) 
     # Add the button sprites to the sprite group. 
     self.all_sprites.add(self.start_button, self.quit_button) 

    def quit_game(self): 
     """Callback method to quit the game.""" 
     self.done = True 

    def increment_number(self): 
     """Callback method to increment the number.""" 
     self.number += 1 
     print(self.number) 

    def run(self): 
     while not self.done: 
      self.dt = self.clock.tick(30)/1000 
      self.handle_events() 
      self.run_logic() 
      self.draw() 

    def handle_events(self): 
     for event in pg.event.get(): 
      if event.type == pg.QUIT: 
       self.done = True 
      for button in self.all_sprites: 
       button.handle_event(event) 

    def run_logic(self): 
     self.all_sprites.update(self.dt) 

    def draw(self): 
     self.screen.fill((30, 30, 30)) 
     self.all_sprites.draw(self.screen) 
     pg.display.flip() 


if __name__ == '__main__': 
    pg.init() 
    Game(screen).run() 
    pg.quit() 

Addendum 2: Eine Zwischenlösung mit Tasten als Wörterbücher. Es wäre auch möglich, Listen zu verwenden, aber Wörterbücher sind besser lesbar.

import pygame 


pygame.init() 

WHITE = (255, 255, 255) 
ACTIVE_COLOR = pygame.Color('dodgerblue1') 
INACTIVE_COLOR = pygame.Color('dodgerblue4') 
FONT = pygame.font.Font(None, 50) 


def draw_button(button, screen): 
    """Draw the button rect and the text surface.""" 
    pygame.draw.rect(screen, button['color'], button['rect']) 
    screen.blit(button['text'], button['text rect']) 


def create_button(x, y, w, h, text, callback): 
    """A button is a dictionary that contains the relevant data. 

    Consists of a rect, text surface and text rect, color and a 
    callback function. 
    """ 
    # The button is a dictionary consisting of the rect, text, 
    # text rect, color and the callback function. 
    text_surf = FONT.render(text, True, WHITE) 
    button_rect = pygame.Rect(x, y, w, h) 
    text_rect = text_surf.get_rect(center=button_rect.center) 
    button = { 
     'rect': button_rect, 
     'text': text_surf, 
     'text rect': text_rect, 
     'color': INACTIVE_COLOR, 
     'callback': callback, 
     } 
    return button 


def main(): 
    screen = pygame.display.set_mode((640, 480)) 
    clock = pygame.time.Clock() 
    done = False 

    number = 0 

    def increment_number(): # A callback function for the button. 
     """Increment the `number` in the enclosing scope.""" 
     nonlocal number 
     number += 1 
     print(number) 

    def quit_game(): # A callback function for the button. 
     nonlocal done 
     done = True 

    button1 = create_button(100, 100, 250, 80, 'Click me!', increment_number) 
    button2 = create_button(100, 200, 250, 80, 'Me too!', quit_game) 
    # A list that contains all buttons. 
    button_list = [button1, button2] 

    while not done: 
     for event in pygame.event.get(): 
      if event.type == pygame.QUIT: 
       done = True 
      # This block is executed once for each MOUSEBUTTONDOWN event. 
      elif event.type == pygame.MOUSEBUTTONDOWN: 
       # 1 is the left mouse button, 2 is middle, 3 is right. 
       if event.button == 1: 
        for button in button_list: 
         # `event.pos` is the mouse position. 
         if button['rect'].collidepoint(event.pos): 
          # Increment the number by calling the callback 
          # function in the button list. 
          button['callback']() 
      elif event.type == pygame.MOUSEMOTION: 
       # When the mouse gets moved, change the color of the 
       # buttons if they collide with the mouse. 
       for button in button_list: 
        if button['rect'].collidepoint(event.pos): 
         button['color'] = ACTIVE_COLOR 
        else: 
         button['color'] = INACTIVE_COLOR 

     screen.fill(WHITE) 
     for button in button_list: 
      draw_button(button, screen) 
     pygame.display.update() 
     clock.tick(30) 


main() 
pygame.quit() 
+0

Vielen Dank !! Und ja, ich werde mehrere Knöpfe machen, damit ich Hilfe über dein OOP-Beispiel benötige. –

+0

OOP-Beispiel hinzugefügt. Wenn Sie noch nicht mit Pygame-Sprites und Gruppen gearbeitet haben, werfen Sie einen Blick auf [dieses Tutorial] (http://programarcadegames.com/index.php?chapter=introduction_to_sprites&lang=en#section_13). – skrx

+0

Vielen Dank skrx, aber es gibt ein Problem mit mir, meine Hauptsprache ist nicht Englisch und ich kenne Klassen nicht zu viel. Ich lerne immer noch, also kann ich die nicht zu sehr verstehen. Aber ich werde versuchen, es herauszufinden. Übrigens, gibt es eine Chance, jederzeit Hilfe von meinen Codes zu nehmen? –

Verwandte Themen