{"id":"events-mouse","slug":"mouse","title":"The mouse","text":"A mouse click is two events: `pygame.MOUSEBUTTONDOWN` when the button goes down, and `pygame.MOUSEBUTTONUP` when it comes back up. Both have two attributes: `pos`, the point in the window where it happened, as a tuple (x, y), and `button`, which button it was: 1 is the left button, 2 the middle one and 3 the right one.\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEBUTTONDOWN:\n            print(\"button\", event.button, \"at\", event.pos)\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/f8919e417f3af6aa.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":20,\"log\":\"fill (0, 0, 0)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.MOUSEBUTTONDOWN:\\n            print(\\\"button\\\", event.button, \\\"at\\\", event.pos)\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":20,\"events\":[{\"at\":4,\"type\":\"mousedown\",\"pos\":[50,40],\"button\":1},{\"at\":5,\"type\":\"mouseup\",\"pos\":[50,40],\"button\":1},{\"at\":10,\"type\":\"mousedown\",\"pos\":[270,200],\"button\":3},{\"at\":11,\"type\":\"mouseup\",\"pos\":[270,200],\"button\":3}]}}\n```\n\n```output\nbutton 1 at (50, 40)\nbutton 3 at (270, 200)\n```\n\n`event.pos[0]` is the x of the click and `event.pos[1]` its y.\n\n### Drawing where the player clicks\n\nThis program moves the robot to each click. The click should be at the robot's center, so its top-left corner goes half its size to the left and up:\n\n```asset robot.png\n40x40\n```\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nrobot = pygame.image.load(\"robot.png\")\n\nx = 0\ny = 0\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEBUTTONDOWN:\n            x = event.pos[0] - robot.get_width() // 2\n            y = event.pos[1] - robot.get_height() // 2\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(robot, (x, y))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/e5d24fb26838afd3.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit robot.png at (80, 130)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\nrobot = pygame.image.load(\\\"robot.png\\\")\\n\\nx = 0\\ny = 0\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.MOUSEBUTTONDOWN:\\n            x = event.pos[0] - robot.get_width() // 2\\n            y = event.pos[1] - robot.get_height() // 2\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    window.blit(robot, (x, y))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[{\"at\":5,\"type\":\"mousedown\",\"pos\":[200,60],\"button\":1},{\"at\":6,\"type\":\"mouseup\",\"pos\":[200,60],\"button\":1},{\"at\":15,\"type\":\"mousedown\",\"pos\":[100,150],\"button\":1},{\"at\":16,\"type\":\"mouseup\",\"pos\":[100,150],\"button\":1}]},\"assets\":[\"robot.png\"]}\n```\n\nThe click only changes the variables. The drawing still happens in every frame, from the variables, as in every program so far. To keep something for every click, such as a coin at each one, append the points to a list and draw everything in the list in each frame.\n\n### Following the mouse\n\n`pygame.MOUSEMOTION` comes whenever the mouse moves over the window, and its `pos` is where the mouse pointer is now. Here the robot's center sticks to the pointer:\n\n```python\nimport pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nrobot = pygame.image.load(\"robot.png\")\n\nx = 140\ny = 100\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEMOTION:\n            x = event.pos[0] - robot.get_width() // 2\n            y = event.pos[1] - robot.get_height() // 2\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(robot, (x, y))\n    pygame.display.flip()\n```\n\n```window-shot\n{\"image\":\"lessons/windows/adbfee807464efb5.png\",\"size\":[320,240],\"title\":\"pygame window\",\"frames\":30,\"log\":\"fill (0, 0, 0)\\nblit robot.png at (240, 70)\",\"ended\":\"closed\",\"code\":\"import pygame\\n\\npygame.init()\\nwindow = pygame.display.set_mode((320, 240))\\nrobot = pygame.image.load(\\\"robot.png\\\")\\n\\nx = 140\\ny = 100\\n\\nwhile True:\\n    for event in pygame.event.get():\\n        if event.type == pygame.MOUSEMOTION:\\n            x = event.pos[0] - robot.get_width() // 2\\n            y = event.pos[1] - robot.get_height() // 2\\n        if event.type == pygame.QUIT:\\n            exit()\\n\\n    window.fill((0, 0, 0))\\n    window.blit(robot, (x, y))\\n    pygame.display.flip()\\n\",\"window\":{\"frames\":30,\"events\":[{\"at\":5,\"type\":\"mousemove\",\"pos\":[180,120]},{\"at\":10,\"type\":\"mousemove\",\"pos\":[220,110]},{\"at\":15,\"type\":\"mousemove\",\"pos\":[260,90]}]},\"assets\":[\"robot.png\"]}\n```\n\nInstead of jumping, the robot can also walk toward the pointer: keep the point it's heading for in variables of its own, and in each frame move the robot a step closer to it, as the second challenge below does.\n\n### Did the click hit?\n\nA click hits an image when its point is inside the image's rectangle. The image covers x from its left edge up to (but not including) its left edge plus its width, and the same for y:\n\n```python\nmouse_x, mouse_y = event.pos\nif x <= mouse_x < x + robot.get_width() and y <= mouse_y < y + robot.get_height():\n    print(\"Got the robot!\")\n```\n\nPython lets you chain comparisons like this: `x <= mouse_x < x + 40` means `x <= mouse_x and mouse_x < x + 40`.\n","task":"Each click should leave a coin centered at the point of the click. Fill in the blanks: the type of event for a press of the mouse button, and the attribute that has the point.\n","kind":"fill","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\ncoin = pygame.image.load(\"coin.png\")\n\ncoins = []\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.[[MOUSEBUTTONDOWN]]:\n            x, y = event.[[pos]]\n            coins.append((x - 12, y - 12))\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for place in coins:\n        window.blit(coin, place)\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\ncoin = pygame.image.load(\"coin.png\")\n\ncoins = []\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEBUTTONDOWN:\n            x, y = event.pos\n            coins.append((x - 12, y - 12))\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    for place in coins:\n        window.blit(coin, place)\n    pygame.display.flip()\n","hints":["The event comes when a mouse button goes down. Its point is a tuple (x, y).","The type is `pygame.MOUSEBUTTONDOWN`, and the point is `event.pos`."],"tests":[{"name":"Three clicks","stdin":"","window":{"frames":20,"events":[{"at":3,"type":"mousedown","pos":[40,50],"button":1},{"at":4,"type":"mouseup","pos":[40,50],"button":1},{"at":8,"type":"mousedown","pos":[160,120],"button":1},{"at":9,"type":"mouseup","pos":[160,120],"button":1},{"at":13,"type":"mousedown","pos":[280,200],"button":1},{"at":14,"type":"mouseup","pos":[280,200],"button":1}]},"expectWindow":{"frames":20,"log":"fill (0, 0, 0)\nblit coin.png at (28, 38)\nblit coin.png at (148, 108)\nblit coin.png at (268, 188)","size":[320,240],"title":"pygame window","image":"lessons/windows/49a399ba9bbb6c29.png"},"expect":"","hidden":false},{"name":"Test 2","stdin":"","window":{"frames":15,"events":[{"at":2,"type":"mousedown","pos":[100,30],"button":1},{"at":3,"type":"mouseup","pos":[100,30],"button":1},{"at":6,"type":"mousedown","pos":[60,180],"button":1},{"at":7,"type":"mouseup","pos":[60,180],"button":1}]},"expectWindow":{"frames":15,"log":"fill (0, 0, 0)\nblit coin.png at (88, 18)\nblit coin.png at (48, 168)","size":[320,240],"title":"pygame window","image":"lessons/windows/08e6450d3f0b146f.png"},"expect":"","hidden":true},{"name":"Still pressed at the end","stdin":"","window":{"frames":15,"events":[{"at":2,"type":"mousedown","pos":[200,40],"button":1},{"at":2,"type":"mouseup","pos":[200,40],"button":1},{"at":9,"type":"mousedown","pos":[120,160],"button":1}]},"expectWindow":{"frames":15,"log":"fill (0, 0, 0)\nblit coin.png at (188, 28)\nblit coin.png at (108, 148)","size":[320,240],"title":"pygame window","image":"lessons/windows/4650025c47052aee.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"assets":["coin.png"],"more":[{"task":"The robot walks toward the mouse pointer. It starts at (0, 0) and heads for the point that puts its center at the pointer: (pointer's x - 20, pointer's y - 20). In each frame it moves 2 pixels closer across (if it isn't there yet) and 2 pixels closer up or down (if it isn't there yet). Until the mouse moves, it heads for (0, 0), so it stays put. Use a clock at 60 frames per second, and handle the events, move and draw in that order. (The tests move the mouse only to points with even numbers, so the robot always ends exactly there.)\n","kind":"code","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nrobot = pygame.image.load(\"robot.png\")\n\nx = 0\ny = 0\nclock = pygame.time.Clock()\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(robot, (x, y))\n    pygame.display.flip()\n    clock.tick(60)\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nrobot = pygame.image.load(\"robot.png\")\n\nx = 0\ny = 0\ntarget_x = 0\ntarget_y = 0\nclock = pygame.time.Clock()\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEMOTION:\n            target_x = event.pos[0] - robot.get_width() // 2\n            target_y = event.pos[1] - robot.get_height() // 2\n        if event.type == pygame.QUIT:\n            exit()\n\n    if x < target_x:\n        x += 2\n    if x > target_x:\n        x -= 2\n    if y < target_y:\n        y += 2\n    if y > target_y:\n        y -= 2\n\n    window.fill((0, 0, 0))\n    window.blit(robot, (x, y))\n    pygame.display.flip()\n    clock.tick(60)\n","hints":["Keep the point the robot heads for in two variables of its own, `target_x` and `target_y`, which start at 0. A `pygame.MOUSEMOTION` event sets them.","After the event loop, compare the robot's position with the target: if `x < target_x`, x grows by 2; if `x > target_x`, it shrinks by 2. The same for y.","Draw the robot at its own position, not at the target."],"tests":[{"name":"The mouse moves once","stdin":"","window":{"frames":120,"events":[{"at":5,"type":"mousemove","pos":[200,150]}]},"expectWindow":{"frames":120,"log":"fill (0, 0, 0)\nblit robot.png at (180, 130)","size":[320,240],"title":"pygame window","image":"lessons/windows/e2be3108dd32e91c.png"},"expect":"","hidden":false},{"name":"The mouse moves twice","stdin":"","window":{"frames":150,"events":[{"at":5,"type":"mousemove","pos":[300,40]},{"at":60,"type":"mousemove","pos":[60,200]}]},"expectWindow":{"frames":150,"log":"fill (0, 0, 0)\nblit robot.png at (40, 180)","size":[320,240],"title":"pygame window","image":"lessons/windows/461a143875b6ccb2.png"},"expect":"","hidden":true},{"name":"Still on its way","stdin":"","window":{"frames":30,"events":[{"at":2,"type":"mousemove","pos":[160,120]}]},"expectWindow":{"frames":30,"log":"fill (0, 0, 0)\nblit robot.png at (56, 56)","size":[320,240],"title":"pygame window","image":"lessons/windows/915ca84d8edceb43.png"},"expect":"","hidden":true}],"require":[],"forbid":[],"assets":["robot.png"]},{"task":"Whack the monster! The monster appears at the first of these places, its top-left corner given:\n\n```python\nplaces = [(40, 40), (240, 160), (140, 100), (240, 40)]\n```\n\nWhen the player clicks with the left button (button 1) on the monster, that's a hit, and the monster jumps to the next place in the list; after the last place it goes back to the first. A click that misses the monster, or a click with another button, does nothing. The monster is 40 x 40, and a click hits it when it's inside the monster's rectangle, as in the lesson: a click on the pixel just past its right or bottom edge misses.\n\nWhen the window is closed, the program prints the number of hits as `Hits: 2` and ends: in the `pygame.QUIT` branch, print the hits and then call `exit()`. (Do it there, not after the main loop. Here a closed window can't show another frame, so a program that draws again after `pygame.QUIT` is stopped at its next `pygame.display.flip()`, and code after its loop never runs.)\n","kind":"code","seed":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nmonster = pygame.image.load(\"monster.png\")\n\nplaces = [(40, 40), (240, 160), (140, 100), (240, 40)]\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.QUIT:\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(monster, places[0])\n    pygame.display.flip()\n","solution":"import pygame\n\npygame.init()\nwindow = pygame.display.set_mode((320, 240))\nmonster = pygame.image.load(\"monster.png\")\n\nplaces = [(40, 40), (240, 160), (140, 100), (240, 40)]\nplace = 0\nhits = 0\n\nwhile True:\n    for event in pygame.event.get():\n        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:\n            x, y = places[place]\n            mouse_x, mouse_y = event.pos\n            if x <= mouse_x < x + 40 and y <= mouse_y < y + 40:\n                hits += 1\n                place = (place + 1) % len(places)\n        if event.type == pygame.QUIT:\n            print(\"Hits:\", hits)\n            exit()\n\n    window.fill((0, 0, 0))\n    window.blit(monster, places[place])\n    pygame.display.flip()\n","hints":["Keep the index of the monster's place in a variable, and draw the monster at `places[index]`. Keep the hits in another variable.","On a `pygame.MOUSEBUTTONDOWN` event with `event.button == 1`, take the monster's corner `x, y` and the click's `event.pos`, and check `x <= mouse_x < x + 40` and the same for y.","On a hit, add one to the hits and move to the next place: `(index + 1) % len(places)` goes back to 0 after the last one. Print the hits in the `QUIT` branch, before `exit()`."],"tests":[{"name":"Two hits and a miss","stdin":"","window":{"frames":30,"events":[{"at":3,"type":"mousedown","pos":[50,50],"button":1},{"at":4,"type":"mouseup","pos":[50,50],"button":1},{"at":8,"type":"mousedown","pos":[100,100],"button":1},{"at":9,"type":"mouseup","pos":[100,100],"button":1},{"at":13,"type":"mousedown","pos":[270,190],"button":1},{"at":14,"type":"mouseup","pos":[270,190],"button":1}]},"expectWindow":{"frames":30,"log":"fill (0, 0, 0)\nblit monster.png at (140, 100)","size":[320,240],"title":"pygame window","image":"lessons/windows/70adb8b484e15030.png"},"expect":"Hits: 2","hidden":false},{"name":"All the way round","stdin":"","window":{"frames":40,"events":[{"at":2,"type":"mousedown","pos":[40,40],"button":1},{"at":3,"type":"mouseup","pos":[40,40],"button":1},{"at":6,"type":"mousedown","pos":[279,199],"button":1},{"at":7,"type":"mouseup","pos":[279,199],"button":1},{"at":10,"type":"mousedown","pos":[160,120],"button":1},{"at":11,"type":"mouseup","pos":[160,120],"button":1},{"at":14,"type":"mousedown","pos":[250,50],"button":1},{"at":15,"type":"mouseup","pos":[250,50],"button":1},{"at":18,"type":"mousedown","pos":[79,79],"button":1}]},"expectWindow":{"frames":40,"log":"fill (0, 0, 0)\nblit monster.png at (240, 160)","size":[320,240],"title":"pygame window","image":"lessons/windows/0e2b85c0de7d181f.png"},"expect":"Hits: 5","hidden":true},{"name":"The right button","stdin":"","window":{"frames":15,"events":[{"at":3,"type":"mousedown","pos":[60,60],"button":3},{"at":3,"type":"mouseup","pos":[60,60],"button":3},{"at":8,"type":"mousedown","pos":[260,180],"button":1},{"at":8,"type":"mouseup","pos":[260,180],"button":1}]},"expectWindow":{"frames":15,"log":"fill (0, 0, 0)\nblit monster.png at (40, 40)","size":[320,240],"title":"pygame window","image":"lessons/windows/cc2e076b967c3481.png"},"expect":"Hits: 0","hidden":true},{"name":"Just outside the edges","stdin":"","window":{"frames":20,"events":[{"at":2,"type":"mousedown","pos":[80,60],"button":1},{"at":2,"type":"mouseup","pos":[80,60],"button":1},{"at":6,"type":"mousedown","pos":[60,80],"button":1},{"at":6,"type":"mouseup","pos":[60,80],"button":1},{"at":10,"type":"mousedown","pos":[39,60],"button":1},{"at":10,"type":"mouseup","pos":[39,60],"button":1},{"at":14,"type":"mousedown","pos":[60,39],"button":1},{"at":14,"type":"mouseup","pos":[60,39],"button":1}]},"expectWindow":{"frames":20,"log":"fill (0, 0, 0)\nblit monster.png at (40, 40)","size":[320,240],"title":"pygame window","image":"lessons/windows/cc2e076b967c3481.png"},"expect":"Hits: 0","hidden":true}],"require":[],"forbid":[],"assets":["monster.png"]}]}