Below is some code I'm testing to integrate Tkinter and Pygame together. I've managed to get a pygame display inside a Tkinter window, and also show a button ontop of this pygame display. The button simply draws a white circle. The problem starts immediately by not drawing Tkinter's configured cursor (X_cursor) when the program starts. Then, when I mouse over the button and back off of it the cursor starts to flash to pygame's default cursor and then back to Tkinter's configured cursor. Also, the cursor only reverts to pygame's default if the mouse is in motion. Otherwise, it's the correct "X_Cursor".
I'm having a hard time understanding Tkinter's system, and I'm sure the solution is in my face. I just need a little help figuring it out.
import pygame
import pygame.key
from pygame.locals import *
import tkinter as tk
from tkinter import *
import os
#colors#
BLACK = (0,0,0)
WHITE = (255, 255, 255)
GREEN = (0, 255,0)
RED = (255, 0,0)
BLUE = (0,0, 255)
#buttons#
mButton1 = (1, 0, 0)
mButton2 = (0, 1, 0)
mButton3 = (0, 0, 1)
root = tk.Tk()
root.attributes('-fullscreen', True)
root.title("This title isn't visible since it's fullscreen")
root.config(cursor = "X_cursor")
embed = tk.Frame(root, width = 1920, height = 1080) #creates embed frame for pygame window
embed.grid(columnspan = 10, rowspan = 10) # Adds grid
os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
worldWindow = pygame.display.set_mode((0,0), RESIZABLE)
worldWindow.fill(BLACK)
def draw():
pygame.draw.circle(worldWindow, WHITE, (250,250), 125)
IMAGEOBJECT = PhotoImage( file = 'TESTIMAGE.gif')
buttonwin = tk.Frame(root, width = 75, height = 75)
buttonwin.grid(row =8, column = 8)
button1 = Button(root, image = IMAGEOBJECT,text = "Draw a circle", cursor = "circle", command=draw)
button1.grid(row =8 , column = 8)
pygame.display.init()
#loop until user clicks close button
done = False
clock = pygame.time.Clock()
#~~~~~~~~MAIN LOOP~~~~~~~~#
while not done:
for event in pygame.event.get():
if (event.type == pygame.QUIT) or (event.type == pygame.KEYDOWN and pygame.K_ESCAPE):
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
mButton = pygame.mouse.get_pressed()
if mButton == mButton1:
pos = pygame.mouse.get_pos()
print(pos)
else:
pygame.event.clear()
#limit to 60 frames per second
clock.tick(60)
#update the screen with all the draws
pygame.display.update()
root.update()
pygame.quit()
It appears to be tkinter and pygame 'fighting' over who shows the cursor. I added this to the pygame event loop to check.
elif event.type == pygame.MOUSEMOTION:
pygame.mouse.set_cursor(*pygame.cursors.diamond)
The behavior stays the same, now showing the diamond cursor while moving and 'X' Cursor when stopped. When I comment out the tkinter cursor config, I then get the windows os default cursor when motion is stopped.
The easiest solution in my mind would be to load the tkinter 'X' into pygame cursor compiler and use that as its default or under the Mouse motion event (details in link).
https://www.pygame.org/docs/ref/cursors.html
Another option I kinda liked the look of, would be to use this cursor in the mouse motion elif.
*pygame.cursors.broken_x
Related
I have some issues with the fullscreen option of pygame. Here is some code that simply draws a blue window and by pressing R we can switch between blue and purple. Then we can also toggle between fullscreen and windowed mode using F or G. F is implemented explicitly and G uses the method toggle_fullscreen().
import pygame, sys
from pygame.locals import *
#Initializes pygame
pygame.init()
#Defines the Clock object
clock = pygame.time.Clock()
#Just draws a blue screen
size = (960, 540)
blue = (0,0,100)
purp = (100,0,100)
is_blue = True
display_surf = pygame.display.set_mode(size, RESIZABLE)
display_surf.fill(blue)
mainLoop = True
is_fullscreen = False
#Mainloop
while mainLoop:
dt = clock.tick(12)
for event in pygame.event.get():
if event.type == pygame.QUIT:
mainLoop = False
if event.type == pygame.KEYDOWN:
#Single key pressed
if event.key == K_f:
#Toggles fullscreen
is_fullscreen = not is_fullscreen
old_surface = display_surf
setmode = FULLSCREEN if is_fullscreen else RESIZABLE
display_surf = pygame.display.set_mode(size, setmode)
display_surf.blit(old_surface, (0,0))
del old_surface
if event.key == K_q:
#Quits the app
mainLoop = False
if event.key == K_r:
#Redraws the blue or purple
print("Trying to flip colors")
display_surf.fill(purp if is_blue else blue)
is_blue = not is_blue
if event.key == K_g:
#Toggles fullscreen with the dedicated method
is_fullscreen = not is_fullscreen
pygame.display.toggle_fullscreen()
pygame.display.update()
pygame.quit()
I am on Ubuntu 18.04 using Python 3.6.8. Here are my observations:
Using pygame 2.0.0.dev6, when going fullscreen with either F or G the screen does the following:
flashes a few times
goes in the task bar as a minimized icon
if I click on the icon the screen flashes a few more times and finally we are fullscreen
problem: the screen is entirely black and the button R does not flip the colors (but prints the message)
Still using pygame 2.0.0.dev6. In this case the G and the F button behave differently: when going back from fullscreen to windowed with G, the R button doesn't flip the colors, even in the windowed version. When using F instead it works.
With pygame version 2.0.0.dev3 the G button does not work at all, while F has the same behavior as before.
My major problem is 1.4.: the fullscreen mode is entirely black.
Now let's do a modification. Change the following line in the code for the F button
setmode = FULLSCREEN|SCALED if is_fullscreen else RESIZABLE #FULLSCREEN -> FULLSCREEN|SCALED
This goes fullscreen with the current screen resolution and not the one I specify at the top. Now the problems 1.1., 1.2 and 1.3. are gone: the app goes to fullscreen immediately. But the problem 1.4. persists and furthermore the program does not accept inputs anymore. If I press Q it won't quit. It doesn't take Alt+Tab or Alt+F4 and so I have to restart the computer.
pygame.display.set_mode creates a pygame.Surface object, which is associated to the window. When pygame.display.set_mode() is called a again, then the object which was associated to the surface before gets invalide.
You've to copy() the "old" surface:
is_fullscreen = not is_fullscreen
old_surface = display_surf.copy()
setmode = FULLSCREEN if is_fullscreen else RESIZABLE
display_surf = pygame.display.set_mode(size, setmode)
display_surf.blit(old_surface, (0,0))
So, i am building a physics simulation. And whenever the left mouse button is pressed, the player can control the height the ball is at(by dragging). What i want to build is: whenever the player is dragging the ball to reposition it, a screen will appear at the side, and inside it i want a real time zoom of where the ball is(sorry if i didnt explain it too well, i think the picture explains it better).
Just to clarify, I want it all in one window only
Can anyone help me? :)
It's not that complicated. Just draw your stuff on a seperate Surface, then use subsurface and the transform module. Here's an example:
import pygame
pygame.init()
def main():
# we'll not draw an the display surface directly
screen = pygame.display.set_mode((800, 600))
# but we'll draw everything on this surface
main = screen.copy()
# this surface is the zoom window
zoom = pygame.Surface((400, 300))
# the ball and its movement vector
ball = pygame.Rect(550, 100, 40, 40)
ball_v = pygame.Vector2(0, 0)
dt = 0
clock = pygame.time.Clock()
while True:
pressed = pygame.mouse.get_pressed()
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
if pressed[0]:
# dragging the ball if mouse button is pressed
ball_v = pygame.Vector2(0, 0)
ball.center = pygame.mouse.get_pos()
else:
# if not, we apply the gravity
ball_v += pygame.Vector2(0, 0.2)
# move the ball
ball.move_ip(ball_v)
# but keep it on the screen
ball.clamp_ip(main.get_rect())
# draw the main surface
main.fill(pygame.Color('white'))
w = 10
for y in range(main.get_rect().height):
pygame.draw.rect(main, pygame.Color('lightgray'), pygame.Rect(500, y, w, 1))
w = (w - 1) % 20
pygame.draw.circle(main, pygame.Color('orange'), ball.center, 20)
screen.blit(main, (0, 0))
# if the mouse button is pressed, draw the zoom window
if pressed[0]:
# the area of the main surface that should be drawn in the zoom window
rect = pygame.Rect(0, 0, 200, 150)
# center it on the ball
rect.center = ball.center
# ensure it's on the screen
rect.clamp_ip(main.get_rect())
# grab the part from the main surface
sub = main.subsurface(rect)
# scale it
zoom.blit(pygame.transform.scale2x(sub), (0, 0))
pygame.draw.rect(zoom, pygame.Color('black'), zoom.get_rect(), 4)
screen.blit(zoom, (25, 25))
pygame.display.flip()
clock.tick(120)
main()
I am currently following a beginner pygame tutorial on YouTube here but even though I copied the code exactly from the tutorial my pygame window only stays open for about a second and then closes.
note: someone asked this question about three years ago here but it didn't fix my problem.
my code is below
import pygame
pygame.init()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption('hello world')
Your script is ending and so pygame closes everything.
You have to create a loop in order for your game to continue running, with a condition to exit the loop.
You also need to initialize the display with pygame.display.init()
import pygame
pygame.init()
pygame.display.init()
win = pygame.display.set_mode((500, 500))
pygame.display.set_caption('hello world')
clock = pygame.time.Clock()
FPS = 60 # Frames per second.
# Some shortcuts for colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
# For example, display a white rect
rect = pygame.Rect((0, 0), (32, 32))
image = pygame.Surface((32, 32))
image.fill(WHITE)
# Game loop
while True:
# Ensure game runs at a constant speed
clock.tick(FPS)
# 1. Handle events
for event in pygame.event.get():
# User pressed the close button ?
if event.type == pygame.QUIT:
quit()
# Close the program. Other methods like 'raise SystemExit' or 'sys.exit()'.
# Calling 'pygame.quit()' won't close the program! It will just uninitialize the modules.
# 2. Put updates to the game logic here
# 3. Render
win.fill(BLACK) # first clear the screen
win.blit(image, rect) # draw whatever you need
pygame.display.flip() # copy to the screen
I've been trying to get pygame to work in tkinter, but all i get is two separate windows where everyone else says it works: https://stackoverflow.com/a/16550818/12221209
But when I run basic pygame code and all I get is a bouncing spaceship icon on the dock- the same with the aliens demo in terminal. Anybody know how to fix this, because it's driving me insane. All I want is a pygame window inside a tkinter window.
import pygame
import tkinter as tkinter
from tkinter import *
(width, height) = (300, 200)
screen = pygame.display.set_mode((width, height))
pygame.display.flip()
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
All I want is a pygame window inside a tkinter window.
I have taken the code you referenced and cleaned it up a bit to better fit Python 3 and PEP8 more closely.
Until you update your question with an example that shows the problem you have described this is the best I can do for you question.
Let me know if you have any questions.
import tkinter as tk
import pygame
import os
def pygame_update_loop():
pygame.display.update()
# Use an after loop to update the pygame window.
# set the time in milliseconds as desired.
root.after(100, pygame_update_loop)
def draw():
pygame.draw.circle(screen, (0, 0, 0), (250, 250), 125)
root = tk.Tk()
pygame_frame = tk.Frame(root, width=500, height=500)
pygame_frame.pack(side='left')
tk.Button(root, text='Draw', command=draw).pack(side='left')
os.environ['SDL_WINDOWID'] = str(pygame_frame.winfo_id())
os.environ['SDL_VIDEODRIVER'] = 'windib'
width, height = 300, 200
screen = pygame.display.set_mode((width, height))
screen.fill(pygame.Color(255, 255, 255))
pygame.display.init()
pygame_update_loop()
root.mainloop()
I'm currently programming a game in Python 3.6 using Pygame.
My game has a start menu, with a button that is highlighted when hovered over.
When the button is clicked, the start menu disappears and the game starts.
The only problem is, the start button remains... I've tried using boolean values and if statements to make it disappear when the menu does, but to no avail.
To make matters worse, the pygame code that allows the window to be closed doesn't work (highlighted by commenting out). Could anyone help me with these small problems? They seem trivial, but I'm new to pygame and I can't seem to find a fix anywhere.
You will need the pictures below to run the program:
This is the photo of the girl.
This is the background photo.
The code:
#Imports
import sys
import pygame
pygame.init() #Initialise Pygame
#RGB colours
GREY = (128,128,128)
WHITE = (255,255,255)
BLACK = (0,0,0)
#fonts
title_font = pygame.font.Font('freesansbold.ttf',72)
menu_font = pygame.font.Font('freesansbold.ttf',32)
#images
girl_img = pygame.image.load('emily.png')
background_img = pygame.image.load('tech_lab.png')
class Game: #Game Class
def __init__(self): #Constructor
self.size = width, height = (1000,563)
self.screen = pygame.display.set_mode(self.size)
def menu_screen(self): #Menu Screen Function
intro = True
while intro:
## for event in pygame.event.get(): #To close window
## print(event)
## if event.type == pygame.QUIT:
## pygame.quit()
## sys.exit()
self.screen.fill(GREY) #Set background colour
self.screen.blit(title_font.render('EXAMPLE', True, BLACK), (330,100)) #Display game title
start_button = pygame.draw.rect(self.screen, (BLACK), pygame.Rect(410,310,180,55)) #Display start button rect
self.screen.blit(menu_font.render('Play', True, GREY), (425,310)) #Display start button text
pygame.display.flip() #Update display
while True:
pygame.event.get() #Get pygame events
click_pos = pygame.mouse.get_pos() #Get mouse position
if 410+180 > click_pos[0] > 410 and 310+55 > click_pos[1] > 310: #If mouse position within start button rect...
pygame.draw.rect(self.screen, (WHITE), pygame.Rect(410,310,180,55)) #then change colour of start button rect
if (pygame.mouse.get_pressed())[0] == 1: #If start button rect clicked...
start_game(self) #Start main game
else:
pygame.draw.rect(self.screen, (BLACK), pygame.Rect(410,310,180,55)) #Otherwise start button rect colour is black
self.screen.blit(menu_font.render('Play', True, GREY), (425,310))#Display start button text
pygame.display.flip() #Update display
def start_game(game): #Main game function
game.screen.blit(background_img, [0,0]) #Display background image
game.screen.blit(girl_img, [80,25]) #Display image
pygame.display.flip() #Update display
def main():
game = Game() #Instantiate class 'game'
game.menu_screen() #Call menu screen function
main()
This loop within a loop thing is going to cause nothing but problems and has to go:
while intro:
....
while True
....
here is some pseudo code:
Class Game:
def menu_screen():
displaying = True
while displaying:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
# evaluate mouse click
if clicked button
displaying = False
self.game()
# drawing code for your images
pygame.display.flip()
def game():
game_running = True
while game_running:
# game running logic and drawing
def main():
# get the Game setup and call main menu
Basically, have your Game class control everything. it will have a menu function which displays the menu. Then they click the button and it goes to a different function within the game class for the actual game.
The start button was remaining because you never cleared the screen when going into the start_game() function. As far as why the close window button wasn't working, it is because of the loop within a loop as i mentioned above. You were trapped in the inner
while True:
loop and you could never get back to the event checking from above. (The quit code itself is fine, although you don't need pygame.quit() because you are quitting the program entirely with sys.ext())
Short of completely reworking your code, this can get you started on the right track.