Jack Jones
Jack Jones

Reputation: 87

how to transition between pygame windows and functions

UPDATED CODE:

import pygame

pygame.init()

width = 700
height = 700
running = True
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Dijkstra's Path-Finding Algorithm Solver")
icon = pygame.image.load('icon.jpg')
pygame.display.set_icon(icon)

def title_main():
    title_font = pygame.font.Font('TrajanPro-Regular.otf', 40)
    text_font = pygame.font.SysFont("comicsans", 30, bold=True)
    Dijkstra_img = pygame.image.load('Dijkstra.jpg')
    text1_display = title_font.render("Dijkstra's Path-Finding", True, (255, 255, 255))
    text2_display = title_font.render('Algorithm', True, (255, 255, 255))
    text3_display = text_font.render('Press any key to continue...', True, (255, 255, 255))
    screen.blit(Dijkstra_img, (225, 40))
    screen.blit(text1_display, (75, 400))
    screen.blit(text2_display, (225, 460))
    screen.blit(text3_display, (190, 550))

def title_game():
    title_font = pygame.font.Font('TrajanPro-Regular.otf', 35)
    title_display = title_font.render('Dijkstra Path-Finding Algorithm', True, (255, 255, 255))
    screen.blit(title_display, (12, 15))

def title_underline():
    rect = pygame.Rect(0, 60, 800, 3)
    rect_display = pygame.draw.rect(screen, [255, 255, 255], rect)
    title_font = pygame.font.Font('TrajanPro-Regular.otf', 100)
    title_display = title_font.render('', True, (255, 255, 255))
    screen.blit(title_display, (270, 198))

def grid():
    grid_x = 0
    grid_y = 63
    blockSize = 20
    for x in range(width):
        for y in range(height):
            rect = pygame.Rect(x*blockSize + grid_x, y*blockSize + grid_y, blockSize, blockSize)
            pygame.draw.rect(screen, (200,200,200), rect, 1)

def game_loop():
     game_is_active = True
     while game_is_active:
         for game_event in pygame.event.get():
            if game_event.type == pygame.QUIT:
                game_is_active = False
            if game_event.type == pygame.MOUSEBUTTONDOWN:
                start = print(pygame.mouse.get_pos())
                print(start)
         screen.fill((0, 0, 0))
         title_game()
         title_underline()
         grid()
         pygame.display.update()

while running:
    for event in pygame.event.get():
        screen.fill((0, 0, 0))
        title_main()
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            running = game_loop()
        pygame.display.update()

I have created a title page for my project under a function that I run when I start the GUI, but what I want to do is that when the user presses any key, I want to NOT display the title_main() function anymore and just display my main application instead. I used KEYDOWN to try to do this but for some reason, its very slow and non-responsive and also the title_main() function still is displayed.

Upvotes: 2

Views: 537

Answers (2)

Kingsley
Kingsley

Reputation: 14906

@Cribber explains why your existing loop is not working.

I would like to provide an answer where only a single event-loop is used because I believe having a simple main-loop is the best architecture for an event-driven program (i.e.: all PyGame programs).

Your program is divided into two states, showing the "title page" and "main application". I would keep this state in a variable, and determine which path to take in the main loop based on it. At each step of the draw → input → update cycle, examine the state (iff necessary) and act based on the value.

# Function definitions as per question

# States the app can be in
TITLE_PAGE = 10   # TODO: maybe use an Enumerated Type
MAIN_APP   = 20

app_state = TITLE_PAGE  # start with the titles

while running:
    # Handle user input
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if ( app_state == TITLE_PAGE ):
                # user dismissed title page, switch to app-mode
                app_state = MAIN_APP

    # Update the screen
    screen.fill((0, 0, 0))
    if ( app_state == TITLE_PAGE ):   # In Title-page mode?
        title_main()
    elif ( app_state == MAIN_APP ):   # In Main-Application mode?
        title_game()
        title_underline()
        grid()

    pygame.display.update()

Sure, when it's very simple like this, it's not a big deal which way it's implemented. But as the program becomes more and more complex, it helps to lay-out the logic flow in simple steps. There's no duplicated event-loop code blocks, or screen-painting, so (for example) if there's a user-input bug, it can only be in one place.

EDIT: Handling first and second mouse clicks

It's pretty easy to handle two mouse clicks. It's just necessary to remember both the click-count, and where the "previous" click was.

This can be accomplished by storing a single initialised variable first_click, but starting with a value of None. The None is important, because it indicates there has not been a click yet. So it tracks the first/second click, and where the position of the first click.

So...

  • if first_click == None → User has not clicked yet
  • if first_click != None → First click OK, waiting on second.

In your code, first_click is called start. It just needs to be initialised to None.

start = None   # no clicks yet

def game_loop():
     game_is_active = True
     while game_is_active:
         for game_event in pygame.event.get():
            if game_event.type == pygame.QUIT:
                game_is_active = False
            if game_event.type == pygame.MOUSEBUTTONDOWN:
                # Mouse has been clicked
                if ( start == None ):             # Is this a first click?
                    # Save the First click 
                    start = pygame.mouse.get_pos()      
                else:
                    # This is a second click, process both
                    handleTwoClicks( start, pygame.mouse.get_pos() )
                    # reset back to no-clicks-made
                    start = None

Upvotes: 2

Cribber
Cribber

Reputation: 2913

The lines in the block beneath if event.type == pygame.KEYDOWN: only get executed if a button is pressed - and only once per KEYDOWN!

Since you have no separate loop after the initial pygame.KEYDOWN, your while running: continues after the functions title_game(), title_underline() and grid() are executed once. Then the loop starts anew and the function title_main() also gets executed again (which is why it is still displayed).

I think you might want to implement another loop after the KEYDOWN was registered.

You will also have to capture the input in this loop separately as you don't "come back" to the for event in pygame.event.get(): in your while running: loop.

I included a bit more cleaned-up version of the code and implemented a game loop after showing your title_main() as I understood your problem.

while running:  # your loop - display the title and wait for a button to be pressed
    for event in pygame.event.get():  
        screen.fill((0, 0, 0))
        title_main()
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN: 
              # start the actual game upon pressing a button for the first time
              running = game_loop() # stop the loop if the game returns with False
        pygame.display.update()
    

def game_loop():
     game_is_active = True
     while game_is_active :
         for game_event in pygame.event.get():
            if game_event.type == pygame.QUIT:
                  return False  # return to your outer loop
         # game logic - display the title, the underline and the grid: 
         screen.fill((0, 0, 0))
         title_game()
         title_underline()
         grid()
         pygame.display.update() # update screen

Upvotes: 2

Related Questions