How to move a no frame pygame windows when user clicks on it?

Question:

I want to create a pygame window that doesn’t have a frame and that moves when the user clicks on it and moves the mouse.
I tried this script but when I click on the windows, ‘0’ is printed but not ‘1’

Something is wrong in my script.

# coding : utf-8
import pygame
from pygame.locals import *
from random import randint
from os import environ
from math import sqrt
pygame.init()

max_fps = 250

clock = pygame.time.Clock()
window_size_x, window_size_x = 720, 360

infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w / 2)) + ',' + str(int(infos.current_h / 2)) # center the window
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)

def move_window(): # move the windows when custom bar is hold
        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        mouse_x, mouse_y = pygame.mouse.get_pos()
        dist_x , dist_y = mouse_x - window_x, mouse_y - window_y # calculate the distance between mouse and window origin

        for event in pygame.event.get():        
            if event.type != MOUSEBUTTONUP: # while bar is hold
                print('1')
                mouse_x, mouse_y = pygame.mouse.get_pos()
                environ['SDL_VIDEO_WINDOW_POS'] = str(mouse_x - dist_x) + ',' + str(mouse_x - dist_x)
                screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME) # rebuild window

def main():
    run = True
    while run :
        screen.fill((255, 255, 255))

        pygame.display.update()
        clock.tick(60) # build frame with 60 frame per second limitation

        for event in pygame.event.get():
            if event.type == MOUSEBUTTONDOWN:
                print('0')
                move_window()

if __name__ == '__main__':
    main()
Asked By: user10620375

||

Answers:

This code use only one for event loop with MOUSEBUTTONDOWN to set moving = True, MOUSEBUTTONUP to set moving = False and MOUSEMOTION which changes window’s position when moving is True.

After move I use pygame.event.clear(pygame.MOUSEBUTTONUP) to remove this type of events because new window was getting this even and it was stoping window.

import pygame
from os import environ

# --- constants --- (UPPER_CASE_NAMES)

WINDOW_WIDTH = 720
WINDOW_HEIGHT = 360

# --- main ---

def main():
    pygame.init()

    infos = pygame.display.Info()

    environ['SDL_VIDEO_WINDOW_POS'] = '{},{}'.format(infos.current_w//2, infos.current_h//2) # center the window
    screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME)

    moving = False

    clock = pygame.time.Clock()
    run = True

    while run:

        screen.fill((255, 255, 255))
        pygame.display.update()
        clock.tick(60) # build frame with 60 frame per second limitation

        for event in pygame.event.get():
            if event.type == pygame.MOUSEBUTTONDOWN:
                if not moving:
                    print('MOUSEBUTTONDOWN')
                    moving = True

                    # remeber start distance
                    #window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
                    window_x, window_y = map(int, environ['SDL_VIDEO_WINDOW_POS'].split(','))

                    dist_x = event.pos[0] # mouse x
                    dist_y = event.pos[1] # mouse y

            elif event.type == pygame.MOUSEBUTTONUP:
                if moving:
                    print('MOUSEBUTTONUP')
                    moving = False

            elif event.type == pygame.MOUSEMOTION:
                if moving:
                    print('moving')
                    mouse_x, mouse_y = pygame.mouse.get_pos()

                    diff_x = dist_x - mouse_x
                    diff_y = dist_y - mouse_y
                    window_x -= diff_x
                    window_y -= diff_y

                    environ['SDL_VIDEO_WINDOW_POS'] = "{},{}".format(window_x, window_y)
                    screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.NOFRAME) # rebuild window

                    pygame.event.clear(pygame.MOUSEBUTTONUP) # to remove MOUSEBUTTONUP event which stops moving window

if __name__ == '__main__':
    main()
Answered By: furas

Write a function, which moves the window from dependent on a previous mouse position (start_x, start_y) and a mouse position (new_x, new_y)

def move_window(start_x, start_y, new_x, new_y):
        global window_size_x, window_size_y

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)
 
        # Windows HACK
        window_size_x += 1 if window_size_x % 2 == 0 else -1 

        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)

In this function is a very important line:

window_size_x += 1 if window_size_x % 2 == 0 else -1

this line changes the width of the window from alternately by +1 and -1. On Windows systems there seems to be a bug, which ignores the new position parameter, if the size of the window didn’t change.
This "hack" is a workaround, which slightly change the size of the window whenever the position is changed.

A different approach, with no flickering, may look as follows. Note, though, that this version is significantly slower:

def move_window(start_x, start_y, new_x, new_y):
        global window_size_x, window_size_y
        buffer_screen = pygame.Surface((window_size_x, window_size_y))
        buffer_screen.blit(pygame.display.get_surface(), pygame.display.get_surface().get_rect())

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)

        window_size_x += 1 if window_size_x % 2 == 0 else -1 

        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME)
        screen.blit(buffer_screen, buffer_screen.get_rect())
        pygame.display.flip()

Change the position on MOUSEMOTION and MOUSEBUTTONUP:

def main():
    run = True
    pressed = False
    start_pos = (0,0)
    while run :

        # [...]

        for event in pygame.event.get():

            if event.type == MOUSEBUTTONDOWN:
                pressed = True
                start_pos = pygame.mouse.get_pos()

            elif event.type == MOUSEMOTION:
                if pressed:
                    new_pos = pygame.mouse.get_pos()
                    move_window(*start_pos, *new_pos)
                    pygame.event.clear(pygame.MOUSEBUTTONUP)

            elif event.type == MOUSEBUTTONUP:
                pressed = False
                new_pos = pygame.mouse.get_pos()
                move_window(*start_pos, *new_pos)

Full example program:

# coding : utf-8
import pygame
from pygame.locals import *
from os import environ
pygame.init()

clock = pygame.time.Clock()
window_size_x, window_size_y = 720, 360

infos = pygame.display.Info()
environ['SDL_VIDEO_WINDOW_POS'] = str(int(infos.current_w/2)) + ',' + str(int(infos.current_h/2)) 
screen = pygame.display.set_mode((window_size_x, window_size_x), pygame.NOFRAME)

def move_window(start_x, start_y, new_x, new_y): 
        global window_size_x, window_size_y

        window_x, window_y = eval(environ['SDL_VIDEO_WINDOW_POS'])
        dist_x, dist_y = new_x - start_x, new_y - start_y
        environ['SDL_VIDEO_WINDOW_POS'] = str(window_x + dist_x) + ',' + str(window_y + dist_y)

        window_size_x += 1 if window_size_x % 2 == 0 else -1
        screen = pygame.display.set_mode((window_size_x, window_size_y), pygame.NOFRAME) 

def main():
    run = True
    pressed = False
    start_pos = (0,0)
    while run :
        screen.fill((255, 255, 255))
        pygame.display.update()
        clock.tick(60)

        for event in pygame.event.get():
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    run = False

            if event.type == MOUSEBUTTONDOWN:
                pressed = True
                start_pos = pygame.mouse.get_pos()

            elif event.type == MOUSEMOTION:
                if pressed:
                    new_pos = pygame.mouse.get_pos()
                    move_window(*start_pos, *new_pos)
                    pygame.event.clear(pygame.MOUSEBUTTONUP)

            elif event.type == MOUSEBUTTONUP:
                pressed = False
                new_pos = pygame.mouse.get_pos()
                move_window(*start_pos, *new_pos)

if __name__ == '__main__':
    main()

This solution no longer works under Windows systems and with Pygame 2.0. The position of a window can, however, be changed with the WINAPI function MoveWindow:

import pygame
from ctypes import windll

pygame.init()
screen = pygame.display.set_mode((400, 400), pygame.NOFRAME)
clock = pygame.time.Clock()

def moveWin(new_x, new_y):
    hwnd = pygame.display.get_wm_info()['window']
    w, h = pygame.display.get_surface().get_size()
    windll.user32.MoveWindow(hwnd, new_x, new_y, w, h, False)

window_pos = [100, 100]
moveWin(*window_pos)

run = True
while run :
    for event in pygame.event.get():
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                run = False
        elif event.type == pygame.MOUSEMOTION:
            if  pygame.mouse.get_pressed()[0]:
                window_pos[0] += event.rel[0]
                window_pos[1] += event.rel[1]
                moveWin(*window_pos)
    
    screen.fill((255, 255, 255))
    pygame.display.update()
    clock.tick(60)

Answered By: Rabbid76

Here is a version of @Rabbid76’s answer for Pygame 2. Note that this example may break as the module _sdl2.video, used to set the window position, is experimental.
move_window.py

import pygame
from pygame._sdl2.video import Window

start_pos = pygame.Vector2(0, 0) #Initial mouse position
pressed = False #Flag that denotes when the mouse is being continuously pressed down

def move_window(window : Window, start_mouse_pos : pygame.Vector2, new_mouse_pos : pygame.Vector2) -> None:
    """Moves the window by the offset between start_mouse_pos and new_mouse_pos"""
    screen = pygame.display.get_surface()
    buffer_screen = pygame.Surface((window.size[0], window.size[1]))
    buffer_screen.blit(screen, screen.get_rect())

    window_pos_Vec2 = pygame.Vector2(window.position)
    window.position = window_pos_Vec2 + new_mouse_pos - start_mouse_pos

    screen.blit(buffer_screen, buffer_screen.get_rect())
    pygame.display.flip()

def check_event(window : Window, event : pygame.event, move_area : pygame.Rect = pygame.Rect(-1, -1, 1, 1)) -> None:
    """Takes a window and event and updates the window position accordingly. n
       move_area can be used to set what area of the screen can be clicked in order to move the window. n
       move_area defaults to a dummy rect which is then internally changed to the full window."""
    global start_pos, pressed
    if move_area == pygame.Rect(-1, -1, 1, 1):
        move_area = pygame.Rect((0, 0), window.size)

    mouse_pos = pygame.Vector2(pygame.mouse.get_pos())
    if move_area.collidepoint(mouse_pos):
        if event.type == pygame.MOUSEBUTTONDOWN:
            pressed = True
            start_pos = mouse_pos
        elif event.type == pygame.MOUSEMOTION and pressed:
            move_window(window, start_pos, mouse_pos)
        elif event.type == pygame.MOUSEBUTTONUP:
            pressed = False
            move_window(window, start_pos, mouse_pos)
    else:
        pressed = False

And in your main file:

import pygame
from pygame._sdl2.video import Window

screen = pygame.display.set_mode(...)

window = Window.from_display_module()

#...
while True:
    for event in pygame.event.get():
        #...
        move_window.check_event(window, event)
Answered By: xXCoolinXx

The code’s not ready, but I was getting there.
I had to abandon it, but it might help someone save some time.
Press f for full-screen mode. The part that is not done,
supposed to be the window-mode resize part. You must have an in-depth
look at toogle_resize() function for that.
The resize it’s taking full desktop resolution and compare it
to the space between clik (MOUSEBUTTONDOWN) and (MOUSEBUTTONUP).
Or at least that’s how I wanted it to work. Good luck!
Also the code needs to be optimized, it is raw.

    import pyautogui
    import pygame
    import sys
    from pygame.locals import *
    from pyinput.mouse import Controller

Mouse controller gets the position on desktop, but I had to run it once more inside the while loop to get the updated values.

    mouse = Controller()
    standard = current_mouse_position = mouse.position

    pygame.init()
    Silkscreen = False

    blueGray = (73, 111, 135)

    width, height = pyautogui.size()

    w = width / 4
    h = height / 4
    ww = width - w
    hh = height - h

    wi = ww - 4
    hi = hh - 4

Set the background and the flags

    room = pygame.image.load('img.png')

    full_flags = pygame.FULLSCREEN | pygame.SCALED |                
    pygame.NOFRAME
    normal_flags = pygame.NOFRAME | pygame.RESIZABLE |         
    pygame.SCALED

    def toggle_fullscreen(f):
        if f:
            return pygame.display.set_mode((ww, hh), full_flags)

        # pygame.display.set_mode(size, normal_flags)  # uncomment         
        this to see issue being fixed as a workaround
        return pygame.display.set_mode((ww, hh), normal_flags)


    def toggle_resize(click):
        if click:
            return pygame.display.set_mode((ww + movement, hh),         
            normal_flags)  # Expands by resize_y


    # Set up the drawing window
    screen = pygame.display.set_mode((ww, hh), normal_flags)


    def bg():
        screen.blit(room, (0, 0))


    def border():
        pygame.draw.rect(screen, blueGray, (0, 0, ww, hh), 2)  #         
        width = 3

    clock = pygame.time.Clock()

    # Run until the user asks to quit
    running = True

    while running:
        current_mouse_position = mouse.position
        # print(current_mouse_position[0])

        bg()

        mw, mh = pygame.mouse.get_pos()  # 0 - 1439(window size)

In the next line, I checked if the mouse is on the window border. Apply only to the left, right border. You must create code for the top, bottom border. Left, right borders, 3 pixels range each side.

        if mw <= 3 or (mw > wi and mw < ww):
            active = True

        moveOne = 0
        # print("data", moveOne)
        # print(type(moveOne))
        moveTwo = 0

        # event handling
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                running = False
                sys.exit()
            # print(mw, mh)

If the user clicks, the standard variable at the beginning of the code it gets the mouse current position. You must run the active bool and check if it is True if you want to start resizing from the border of the window.
The code is very complicated and needs some optimization, but I bet you gonna get it right. Good luck!;)

            if event.type == pygame.MOUSEBUTTONDOWN:
                standard = current_mouse_position[0]
                print(standard)
            if event.type == pygame.MOUSEBUTTONUP:
                moveTwo = current_mouse_position[0]
                movement = standard - moveTwo
                print("This is:", moveTwo)
                toggle_resize(click=MOUSEBUTTONUP)
                active = False

Full-screen handler from here down. Press f for full screen and back to window mode.

            if event.type == pygame.KEYDOWN:
                Silkscreen = not Silkscreen
                if event.key == K_f:
                    screen = toggle_fullscreen(Silkscreen)
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit()

        border()

        # Flip the display
        pygame.display.flip()
        clock.tick(144)  # framerate

    # Done! Time to quit.
    pygame.quit()'
Answered By: GodDeus
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.