Skip to content
Advertisement

How to zoom in and out of an image pygame and using the mousposition as the center of the zoom

I am having an issue with PyGame i can’t resolve. So: my idea is that I have a map I can zoom in/out on. zooming in works fine. But zooming out shows that the rest of the picture got deleted and only the part of the image that was previously visible on the window exists now. This is my code:

import pygame
from pygame.locals import *
import os

class App:
    def __init__(self):
        self.running = True
        self.size = (800,600)

         #create window
        self.window = pygame.display.set_mode(self.size, pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)

        #create map
        currentdir = os.path.dirname(os.path.realpath(__file__))
        imagedir = currentdir+'/images/europe.png'
        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect()
        self.mapsurface = pygame.Surface(self.size)
        self.mapsurface.blit(pygame.transform.scale(self.map,(self.size)),(0,0))
        self.window.blit(self.mapsurface,(0,0))
        self.scale = 1

        #create window
        pygame.display.flip()

    def on_init(self):
        self.country = Country()

    def on_cleanup(self):
        pygame.quit()
        
    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False
        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.window.blit(pygame.transform.scale(self.map,(event.dict['size'])),(0,0))
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4:
                zoom = 2
                wnd_w,wnd_h = self.window.get_size()
                zoom_size = (round(wnd_w/zoom), round(wnd_h/zoom))
                zoom_area = pygame.Rect(0,0, *zoom_size)
                pos_x,pos_y = pygame.mouse.get_pos()
                zoom_area.center = (pos_x, pos_y)
                zoom_surf = pygame.Surface(zoom_area.size)
                zoom_surf.blit(self.window, (0, 0), zoom_area)
                zoom_surf = pygame.transform.smoothscale(zoom_surf, (wnd_w, wnd_h))
                self.window.blit(zoom_surf, (0, 0))

            elif event.button == 5:
                zoom = 0.5
                wnd_w,wnd_h = self.window.get_size()
                zoom_size = (round(wnd_w/zoom), round(wnd_h/zoom))
                zoom_area = pygame.Rect(0,0,*zoom_size)
                pos_x,pos_y = pygame.mouse.get_pos()
                zoom_area.center = (pos_x, pos_y)
                zoom_surf = pygame.Surface(zoom_area.size)
                zoom_surf.blit(self.window, (0, 0), zoom_area)
                zoom_surf = pygame.transform.smoothscale(zoom_surf, (wnd_w, wnd_h))
                self.window.blit(zoom_surf, (0, 0))
            pygame.display.flip()

        pygame.display.update()
    def on_execute(self):
        while self.running == True:
            for event in pygame.event.get():
                self.check_event(event)
        self.on_cleanup()

class Country(App):
    def __init__(self):
        super().__init__()
    

start = App()
start.on_init()
start.on_execute()

Here are the screenshots of my problem:

so far so good:

enter image description here

zooming in works fine:

enter image description here

zooming out causes this:

enter image description here

Advertisement

Answer

You’ll need to scale and blit the original image when you zoom. Use the attribute maprect to define the scaled size and relative location of the map. Add a method blit map that can scale and blit the map. Use the method in the constructor of the class App:

class App:
    def __init__(self):
        # [...]

        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect(center = self.window.get_rect().center)
        self.blitmap()

        #create window
        pygame.display.flip()

    def blitmap(self):
        self.mapsurface = pygame.transform.smoothscale(self.map, self.maprect.size)
        self.window.fill(0)
        self.window.blit(self.mapsurface, self.maprect)

    # [...]

When the image is zoomed, calculate the new mapping rectangle (self.maprect) and call the method again:

class App:
    # [...]

    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False

        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.blitmap()

        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4 or event.button == 5:
                zoom = 2 if event.button == 4 else 0.5
                mx, my = event.pos
                left   = mx + (self.maprect.left - mx) * zoom
                right  = mx + (self.maprect.right - mx) * zoom
                top    = my + (self.maprect.top - my) * zoom
                bottom = my + (self.maprect.bottom - my) * zoom
                self.maprect = pygame.Rect(left, top, right-left, bottom-top)
                self.blitmap()

See also Scale and zoom window


Complete example:

repl.it/@Rabbid76/PyGame-ZoomInAndOut

import pygame
from pygame.locals import *
import os

class App:
    def __init__(self):
        self.running = True
        self.size = (800,600)

         #create window
        self.window = pygame.display.set_mode(self.size, pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)

        #create map
        currentdir = os.path.dirname(os.path.realpath(__file__))
        imagedir = currentdir+'/images/europe.png'
        self.map =  pygame.image.load(imagedir)
        self.maprect = self.map.get_rect(center = self.window.get_rect().center)
        self.blitmap()
        
        #create window
        pygame.display.flip()

    def blitmap(self):
        self.mapsurface = pygame.transform.smoothscale(self.map, self.maprect.size)
        self.window.fill(0)
        self.window.blit(self.mapsurface, self.maprect)

    def on_init(self):
        self.country = Country()

    def on_cleanup(self):
        pygame.quit()
        
    def check_event(self,event):
        if event.type == pygame.QUIT:
            self.running = False
        
        elif event.type == pygame.VIDEORESIZE:
            self.window = pygame.display.set_mode(event.dict['size'], pygame.DOUBLEBUF | pygame.HWSURFACE | pygame.RESIZABLE)
            self.blitmap()
        
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 4 or event.button == 5:
                zoom = 2 if event.button == 4 else 0.5
                mx, my = event.pos
                left   = mx + (self.maprect.left - mx) * zoom
                right  = mx + (self.maprect.right - mx) * zoom
                top    = my + (self.maprect.top - my) * zoom
                bottom = my + (self.maprect.bottom - my) * zoom
                self.maprect = pygame.Rect(left, top, right-left, bottom-top)
                self.blitmap()

        pygame.display.update()

    def on_execute(self):
        while self.running == True:
            for event in pygame.event.get():
                self.check_event(event)
        self.on_cleanup()

class Country(App):
    def __init__(self):
        super().__init__()
    

start = App()
start.on_init()
start.on_execute()
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement