Last active
January 20, 2016 11:11
-
-
Save Mekire/344cb434a1936075efed to your computer and use it in GitHub Desktop.
Accurate relative mouse movement in a resizable display.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
""" | |
A very simple example showing how to drag an item with the mouse. | |
Dragging in this example uses relative mouse movement. | |
-Written by Sean J. McKiernan 'Mekire' | |
""" | |
from __future__ import division | |
import os | |
import sys | |
import pygame as pg | |
CAPTION = "Drag the Red Square" | |
SCREEN_START_SIZE = (1000, 600) | |
class Character(object): | |
""" | |
A class to represent our lovable red sqaure. | |
""" | |
SIZE = (150, 150) | |
def __init__(self, pos): | |
""" | |
The argument pos corresponds to the center of our rectangle. | |
""" | |
self.rect = pg.Rect((0,0), Character.SIZE) | |
self.rect.center = pos | |
self.true_pos = list(pos) | |
self.click = False | |
def check_click(self, pos, scale): | |
""" | |
This function is called from the event loop to check if a click | |
overlaps with the player rect. | |
pygame.mouse.get_rel must be called on an initial hit so that | |
subsequent calls give the correct relative offset. | |
""" | |
scaled_pos = pos[0]*scale[0], pos[1]*scale[1] | |
if self.rect.collidepoint(scaled_pos): | |
self.click = True | |
pg.mouse.get_rel() | |
def update(self, bound_rect, scale): | |
""" | |
If the square is currently clicked, update its position based on the | |
relative mouse movement. Clamp the rect to the screen. | |
""" | |
if self.click: | |
rel = pg.mouse.get_rel() | |
self.true_pos[0] += rel[0]*scale[0] | |
self.true_pos[1] += rel[1]*scale[1] | |
self.rect.center = self.true_pos | |
if not bound_rect.contains(self.rect): | |
self.rect.clamp_ip(bound_rect) | |
self.true_pos = list(self.rect.center) | |
def draw(self, surface): | |
""" | |
Basic draw function. | |
""" | |
surface.fill(pg.Color("red"), self.rect) | |
class App(object): | |
""" | |
A class to manage our event, game loop, and overall program flow. | |
""" | |
def __init__(self): | |
""" | |
Get a reference to the screen (created in main); define necessary | |
attributes; and create our player (draggable rect). | |
""" | |
self.screen = pg.display.get_surface() | |
self.screen_rect = self.screen.get_rect() | |
self.proxy = self.screen.copy() | |
self.proxy_rect = self.screen_rect.copy() | |
self.clock = pg.time.Clock() | |
self.fps = 60 | |
self.done = False | |
self.keys = pg.key.get_pressed() | |
self.scale = (1, 1) | |
self.player = Character(self.screen_rect.center) | |
def event_loop(self): | |
""" | |
This is the event loop for the whole program. | |
Regardless of the complexity of a program, there should never be a need | |
to have more than one event loop. | |
""" | |
for event in pg.event.get(): | |
if event.type == pg.QUIT or self.keys[pg.K_ESCAPE]: | |
self.done = True | |
elif event.type == pg.MOUSEBUTTONDOWN and event.button == 1: | |
self.player.check_click(event.pos, self.scale) | |
elif event.type == pg.MOUSEBUTTONUP and event.button == 1: | |
self.player.click = False | |
elif event.type in (pg.KEYUP, pg.KEYDOWN): | |
self.keys = pg.key.get_pressed() | |
elif event.type == pg.VIDEORESIZE: | |
self.screen = pg.display.set_mode(event.size, pg.RESIZABLE) | |
self.screen_rect = self.screen.get_rect() | |
self.scale = (self.proxy_rect.w/self.screen_rect.w, | |
self.proxy_rect.h/self.screen_rect.h) | |
def render(self): | |
""" | |
All drawing should be found here. | |
This is the only place that pygame.display.update() should be found. | |
""" | |
size = self.screen_rect.size | |
self.proxy.fill(pg.Color("black")) | |
self.player.draw(self.proxy) | |
pg.transform.smoothscale(self.proxy, size, self.screen) | |
pg.display.update() | |
def main_loop(self): | |
""" | |
This is the game loop for the entire program. | |
Like the event_loop, there should not be more than one game_loop. | |
""" | |
while not self.done: | |
self.event_loop() | |
self.player.update(self.proxy_rect, self.scale) | |
self.render() | |
self.clock.tick(self.fps) | |
def main(): | |
""" | |
Prepare our environment, create a display, and start the program. | |
""" | |
pg.init() | |
pg.display.set_caption(CAPTION) | |
pg.display.set_mode(SCREEN_START_SIZE, pg.RESIZABLE) | |
App().main_loop() | |
pg.quit() | |
sys.exit() | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment