I am an amateur programmer. I have a small (and urgent) problem. I am working on a text (console) based adventure game for fun. At a certain point, I want a pygame window to open. The player has to click in the window as fast as possible. The reaction time should be returned to the main program, and the pygame window should close. The main program will then continue running.
I've already written the script for the pygame window and it works fine. My main program also works fine. Now how do I call the pygame window from the main program?
I tried importing the pygame script but that didn't work.
Thanks.
Here's my pygame script:
import pygame, sys, time
from pygame.locals import *
pygame.init()
#Set up window
pygame.event.set_grab(0)
pygame.mouse.set_visible(1)
screen = pygame.display.set_mode((300,200))
shape = screen.convert_alpha()
pygame.display.set_caption("Sniper Alert")
#Colors
WHITE = (255, 255, 255)
BLACK = (0,0,0)
RED = (255, 0, 0)
#Draw on surface object
screen.fill(BLACK)
def alert():
#Create a font
font = pygame.font.Font(None,50)
#Render the text
text = font.render("Sniper Alert", True, RED)
#Create a rectangle
textRect = text.get_rect()
#Center the rectangle
textRect.centerx = screen.get_rect().centerx
textRect.centery = screen.get_rect().centery
#Blit the text
screen.blit(text, textRect)
pygame.display.update()
return press()
def press():
t0 = time.clock()
dt = 0
while time.clock() - t0 < 1.5:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
dt = time.clock()- t0
return dt
#Exit
pygame.quit()
sys.exit()
#Run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
There are three ways I can think of. Here they are:
Solution Number 1:
This way is probably the worst solution and the hardest to implement, but lets get it out of the way. I wouldn't advise using it but you may want to in some circumstances. You could use the threading module. Threading is designed for multitasking like this, and would do what you want. You can create a new thread like so:
import threading
def DoSomething(a,b,c): #this function will be called in the background, simultaneously to the main program
#do something here
apple = 1
banana = 2
cat = 3
mythread = threading.thread(target=DoSomething,args=(apple,banana,cat)) #defines a thread
mythread.start() #tells the thread to start running
Solution Number 2
A much better way to do this would be just to launch it as a different program. You could do that with the subprocess module, used for running command line commands. This would effectively run the program as if you had executed it in a new tab of your terminal (without the new tab). You can then make the program able to communicate with yours using subprocess.comunicate(). I will connect the input and output of the pygame program to your main program from here. Here is an example of this:
import subprocess
input = <insert input> #when you run the program and it requires something like a raw_input, this will give it input. You could probably avoid needing to send the pygame program input, because all you really want to do is receive an output from it.
process = subprocess.Popen(["python","<popup filename>"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=False) #this function is how subprocess runs shell/command prompt commands. On a side note, if you simply want to run a shell command without accessing input or output, just use "subprocess.call(<list of words in command (so ls -a would be ['ls','-a'])>)"
output = process.communicate(input)[0] #this will return any output the program prints, meaning you can communicate with the program and receive information from it
Using subprocess is probably the best way.
Solution Number 3
The final option, if you want it all in one file and do not want to mess around with threading, would be just to alternate the two programs in a while loop. Basically, you would run a loop that executes code from both programs. This is how it would work:
while True: #an infinite loop. it doesn't necessarily have to be infinite.
#call methods or run code to maintain text based game
#call methods or run code to maintain pygame window
this has worked just fine for me in a pygame game i made which also had a text component, but the subprocess way is probably better...
Related
I'm trying to create a pygame program with a 2d drawable grid and methods that run for a long time. I want the main game loop to be able to process while the methods are running so I opted for the threading module and it worked fine but I found out that the multiprocessing module is better suited for CPU-heavy programs so I wanted to switch. The code below is an example or representative of my actual code
# importing the necessary libraries
class Board:
# class representing the actual 2d grid or board on screen
class Graph:
# class for drawing methods that take a long time to run
# Graph's methods call q.get() to get the Board object then
# make appropriate changes to it then call q.put() to put it back in the Queue
def draw_board(surface, rects):
# surface: pygame.display
# rects: list of pygame rectangle objects
# draw every rectangle in rects to the display surface.
def main():
# main game loop
board = Board(*args)
q = multiprocessing.Queue()
q.put(board)
graph = Graph(q)
while True:
draw_board(*args)
for event in pygame.event.get():
# checking some conditions and keypresses here
elif event.type == KEYDOWN:
if event.key == pygame.K_r:
t = multiprocessing.Process(target=graph.draw_sth)
t.start()
pygame.display.update()
# fps clock ticks for 60 FPS here
if __name__ == "__main__":
main()
I use multiprocessing.Queue to transfer resources from the main process to the processes spawned inside main() and back. When I run this and click the key "r", nothing happens and the terminal prints the introduction line when main is first called, namely
pygame 2.0.1 (SDL 2.0.14, Python 3.9.5)
Hello from the pygame community. https://www.pygame.org/contribute.html
This doesn't happen when I use threading so I assume that this is due to my usage of Queue or I might have misunderstood and misused multiprocessing instead. Any help is appreciated on the matter. For simplicity, some lines of code have been omitted.
Try calling this function before your event loop. ( I don't know if this will apply to multiprocessing )
def continue_pygame_loop():
pygame.mainloop(0.1)
yield
This post can do a better job of explaining the mechanics behind it : pyGame in a thread
I want to completely prevent the user from closing the Pygame window except for the key x. Currently, I'm able to prevent the user from closing it, but I am unable to prevent the user from opening another window that overlaps it (press windows key -> open chrome, which overlaps the Pygame window).
import sys
import pygame
from pygame.locals import *
pygame.init()
infoObject = pygame.display.Info()
screen = pygame.display.set_mode((infoObject.current_w, infoObject.current_h), flags=pygame.NOFRAME)
while True:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_x:
pygame.quit()
sys.exit()
if event.type == QUIT:
pass
pygame.display.flip()
We don't have complete solutions for this task.
You can make the title var vanish away by setting display mode to NOFRAME (You have already used this): pygame.display.set_mode(flags = pygame.NOFRAME), but this is overkill for just stopping close buttons
Although any user with minimum Computer Knowledge will fire up a Task manager and kill python.exe :-(
There are ways of disabling task manager through Registry in Windows, Image File Execution Options blah blah, but your game script will barely run with Administrator priviliges (With nagging of UAC)
In order to do this, you can add this piece of code to your pygame.display.set_mode():
pygame.display.set_mode(..., flags=pygame.NOFRAME). Now, the bar with the "x" button will disapear.
Notice that if you decide to make this change, you won't be able to move your game window anymore. (Unless you change it back).
i try to load backgroud.png using pygame.image.load(),but i get nothing.here is my code,please help me ,thanks.
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("./images/background.png")
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
while True:
pass
pygame.quit()
here is my sreen:it get nothing
With a game you're making all stuff that needs to be refreshed needs to be in the main game loop, your problem is, is you are drawing the image outside that game loop, meaning it gets drawn once then cleared and never drawn again.
To fix your code this is how you would write it:
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("./images/background.png")
while True:
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
pygame.quit()
But notice how the bg=pygame.image... is outside the loop, this is because if it was inside the loop it would create a new instance of that image every time the loop happens.
The main game loop works by looping through all your functions and other stuff and then doing again and again, and again.
A game loop is how fps works, basically it is the measurement of how many times per second that game loop happens.
Make sure whenever you are doing anything in the loop it actually has a place there, for example loading an image doesn't, but updating where the player is on the screen does.
If you want to have a look at a good game loop that can be applied to most game engines this website will help you. But don't look at the most complex one when using pygame as it isn't built for that. Fix Your Timestep!
But your original problem of the image not loading isn't the case, it was loading but you were drawing your image in the wrong way, if you want a basic tutorial on pygame watch these videos: Game Development in Python 3 With PyGame - 1 - Intro
A better system to ease development
import pygame
bg = None
def load_resources():
bg = pygame.image.load("./images/background.png")
# all other resources
def render():
screen.blit(bg, (0,0))
def update():
# all logic updates for example movement of entities.
### start of game
load_resources()
while True:
update()
render()
pygame.display.update()
pygame.quit()
I think you should not write pass inside while loop because of it the output window will stop responding. Also you should write the screen blit and display.update inside while loop and you should write the correct image extension in the path. You can also write the full path of the file like ==> pygame.image.load(r"C:\Users\Desktop\back_ground.jpg")
import pygame
pygame.init()
# screen
screen = pygame.display.set_mode((480, 700))
# 1.load_image
bg = pygame.image.load("back_ground.jpg")
while True:
# 2.blit
screen.blit(bg, (0, 0))
# 3.update
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
So I'm using Pygame to create a fancy display for a program I am writing. I chose Pygame because it's easy to get started and does a great job with animations. I want the display to be as big as I can make it so as much information can be shown as possible. Here is the kicker however, I still want to be able to get to the console of the program.
Pygame forces a fullscreen window to the front, so you cant tab out, and moving the windows to another windows desktop crashes the display. I would do a key trick to switch the pygame mode, but I cannot use pygame.event.get() because of how the program the threaded.
Is there a way to make it a full-screen window so that I can tab out and leave it up in the background? I dont really want it to just be a normal window because it is not as big that way.
The display crashes after I tab out and back in, here is what that looks like:
I also get a non-zero exit code: -805306369 (0xCFFFFFFF)
Here is a broken down version of the code that still gives me this error, you'll notice there are some things in here you wouldn't have if this was your full program, but I wanted to retain as much architecture as I could.
import pygame
import os
BACKGROUND = (9, 17, 27)
os.environ['SDL_VIDEO_WINDOW_POS'] = "0,0"
pygame.init()
pygame.font.init()
infoObject = pygame.display.Info()
SIZE = (infoObject.current_w, infoObject.current_h)
X_CENTER = SIZE[0]/2
Y_CENTER = SIZE[1]/2
# create a borderless window that's as big as the entire screen
SCREEN = pygame.display.set_mode((SIZE[0], SIZE[1]), pygame.NOFRAME)
clock = pygame.time.Clock()
TextFont = pygame.font.SysFont('Courant', 30)
class DisplayState:
state = type(bool)
def __init__(self):
self.state = True
def get_state(self):
return self.state
def change_state(self, new_state):
self.state = new_state
def main(display_state_object):
running = True
while running:
if display_state_object.get_state():
SCREEN.fill(BACKGROUND)
pygame.display.flip()
else:
return 1
return
if __name__ == "__main__":
main(DisplayState())
EDIT
I think it is a multi-threading problem! See this code:
Produces Error
def start_display():
display(params)
def display(params):
pygame loop
if __name__ == "__main__":
display_thread = threading.Thread(target=start_display)
display_thread.start()
Does not produce error
def display(params):
pygame loop
if __name__ == "__main__":
display_thread = threading.Thread(target=display(params))
display_thread.start
# marker
One problem with the version that does work, the program does not seem to be continuing forwards outside the thread (ie the marker is never reached). Is this how the threading library works? It may explain why I had the middle man function present. Maybe this is a different problem and deserves its own question?
EDIT
Setting up the thread like this allows the main thread to continue, but brings back the pygame error:
threading.Thread(target=display, args=(DisplayState(),))
There's no easy way to do this on windows/sdl using the real fullscreen mode, and the usual way to solve this is to use a borderless window.
Here's how to create such a "fake" fullscreen window in pygame:
import pygame
import os
# you can control the starting position of the window with the SDL_VIDEO_WINDOW_POS variable
os.environ['SDL_VIDEO_WINDOW_POS'] = "0,0"
pygame.init()
# now let's see how big our screen is
info = pygame.display.Info()
# and create a borderless window that's as big as the entire screen
screen = pygame.display.set_mode((info.current_w, info.current_h), pygame.NOFRAME)
You have to call one of the pygame event functions (e.g. pygame.event.pump() or pygame.event.get()) each frame or the window will become unresponsive and the program will appear to have crashed. If you call one of those functions, you should be able to press Alt+Tab (in Windows) to get back to the desktop without crashing the program (if you select the desktop, the window will be minimized and if you select another window, it will just be brought to the front).
def main(display_state_object):
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
# Press Esc to quit.
if event.key == pygame.K_ESCAPE:
running = False
if display_state_object.get_state():
SCREEN.fill(BACKGROUND)
pygame.display.flip()
else:
return 1
return
I've made a pathfinding visualizer using python and pygame. As of now, it can simulate only one algorithm at a time. I want to spawn multiple windows, each simulating different algorithm, side by side so that algorithms can be analyzed against each other. I have a function client.run() that draws the GUI. I'm trying to spawn multiple instances like this:
p=threading.Thread(target = client.run)
q=threading.Thread(target = client.run)
p.start()
q.start()
But by doing so my program hangs! Is there any way to rectify this problem, or any alternative way of running multiple instances/windows?
Pygame is built in a way to have a single window by process, you can't avoid that. The pygame.display module sets you a "display" and that is what you get.
You are in good look, as you have designed you software to work with threads, and have each thread control a display.. Just change the "threading" Python module for the multiprocessing, and use multiprocessing.Process instead of threading.Threads -- as long as you initialize pygame and its display from within each subprocess you should be ok.
I just teste here and teh example bellow works fine:
# -*- coding: utf-8 -*-
import pygame
import multiprocessing
from time import sleep
def init():
pygame.init()
screen = pygame.display.set_mode((320,240))
return screen
def main():
screen = init()
while True:
pygame.event.pump()
event = pygame.event.poll()
if event.type != pygame.NOEVENT:
print event
class Multigame(multiprocessing.Process):
def run(self):
return main()
for i in range(3):
Multigame().start()
while True:
sleep(1)