Wechsel in Untermenü

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
hanseb
User
Beiträge: 2
Registriert: Montag 29. Mai 2023, 21:20

Moin,

leider muss ich doch fragen, auch wenn es mich ärgert. Allerdings komme ich nicht weiter und stehe so langsam ziemlich auf dem Schlauch.

Grundgedanke: Ich habe ein Menü geschrieben, bei dem via klick auf zwei Button's in ein Folgemenü verzweigt wird. Der Übersichtlichkeit habe ich die nicht betroffenen Button's rausgenommen und überflüssigen Funktionen rausgenommen.

test_menu.py

Code: Alles auswählen

import pygame, sys, os
from time import sleep
import test_menu_button


#Variablen
running = True 

# game variable
game_over = False
game_paused = False
menu_state = "start"

#FensterGenerierung
WIDTH = 480*2.5
HEIGHT = 360*2.5
FPS = 60

#Farbgrundsetting
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)

#pygame-Initialisierungen
pygame.init()
pygame.font.init()

screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
pygame.display.set_caption("Tester")#Titel
clock = pygame.time.Clock()

#define fonts
font = pygame.font.SysFont("arialblack",40)

#define colors
TEXT_COL = (255,255,255)

#Buttons
###Verzeichnis der Datei
game_folder = os.path.dirname(__file__)
button_folder = os.path.join(game_folder, 'images/Buttons')
###Laden der Buttons
sPVP_img = pygame.image.load(os.path.join(button_folder, 'Button_Start_PvP.png')).convert_alpha()
sPVE_img = pygame.image.load(os.path.join(button_folder, 'Button_Start_PvE.png')).convert_alpha()
load_img = pygame.image.load(os.path.join(button_folder, 'Button_Load.png')).convert_alpha()
exit_img = pygame.image.load(os.path.join(button_folder, 'Button_Exit.png')).convert_alpha()
back_img = pygame.image.load(os.path.join(button_folder, 'Button_back.png')).convert_alpha()
backg1_img = pygame.image.load(os.path.join(button_folder, 'mountain.png')).convert_alpha()
backg2_img = pygame.image.load(os.path.join(button_folder, 'field.png')).convert_alpha()
backg3_img = pygame.image.load(os.path.join(button_folder, 'clouds.png')).convert_alpha()

# # #Instanzierung der Button
sPVP_button = test_menu3_button.Button(304, 125, sPVP_img, 1)
sPVE_button = test_menu3_button.Button(304, 250, sPVE_img, 1)
load_button = test_menu3_button.Button(304, 375, load_img, 1)
exit_button = test_menu3_button.Button(304, 625, exit_img, 1)
back_button = test_menu3_button.Button(304, 500, back_img, 1)
backg1_button = test_menu3_button.Button(104, 375, backg1_img, 1)
backg2_button = test_menu3_button.Button(304, 375, backg2_img, 1)
backg3_button = test_menu3_button.Button(504, 375, backg3_img, 1)

def draw_text(text, font, text_col, x, y):
    img =font.render(text,True,text_col)
    screen.blit(img,(x,y))

while running:
    #Prüfung der Spiel-Geschwindigkeit
    dt=clock.tick(FPS)

    #Input & Events
    for event in pygame.event.get():
        if event.type ==pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                game_paused = True
        if event.type == pygame.QUIT:
            running = False
    

    #Update Game
    screen.fill(BLUE)

    # # #Startmenu 
    if menu_state == "start":
        #draw start screen buttons
        if sPVP_button.draw(screen):
            print("StartPvP")
            menu_state = "background"
        if sPVE_button.draw(screen):
            print("Start PVE")
            menu_state = "background"
        if load_button.draw(screen):
            print("Show Savings")
        if highscore_button.draw(screen):
            print("Show Highscore")
        if exit_button.draw(screen):
            print("Exit Main")
            running = False

    # # #Background-Menu after PVP or PVE
    if menu_state == "background":
        #draw the different options buttons
        draw_text("Choose your Background", font, TEXT_COL,120,300)
        if backg1_button.draw(screen):
            print("Start Mountain")
        if backg2_button.draw(screen):
            print("Start Field")
        if backg3_button.draw(screen):
            print("Start Cloud")
        if back_button.draw(screen):
            sleep(0.5)
            menu_state ="start"

    #Render Sprites
    pygame.display.flip()
pygame.quit()
sys.exit()

Auch einmal die Button-Klasse

test_menu3_button.py

Code: Alles auswählen

#button class
class Button():
	def __init__(self, x, y, image, scale):
		width = image.get_width()
		height = image.get_height()
		self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale)))
		self.rect = self.image.get_rect()
		self.rect.topleft = (x, y)
		self.clicked = False

	def draw(self, surface):
		action = False
		#get mouse position
		pos = pygame.mouse.get_pos()

		#check mouseover and clicked conditions
		if self.rect.collidepoint(pos):
			if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
				self.clicked = True
				action = True

		if pygame.mouse.get_pressed()[0] == 0:
			self.clicked = False

		#draw button on screen
		surface.blit(self.image, (self.rect.x, self.rect.y))

		return action
Dieser Code klappt. Nun möchte ich den Code aber "vereinfachen" beziehungsweise auslagern. Hierfür gehe ich wie folgt vor.
Main.py

Code: Alles auswählen

import pygame
import sys
import os
import random

from settings import *
from background import *
from menu import *

random.seed()


#Schleife Hauptprogramm

while settings.running:
    #Prüfung der Spiel-Geschwindigkeit
    dt=clock.tick(FPS) 

    #Input & Events
    for event in pygame.event.get():
        if event.type ==pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                game_paused = True        
        if event.type == pygame.QUIT:
            settings.running = False

    #Update Game
    screen.fill(BLUE)
    
    #Render Sprites
    screen.blit(background,backgroundRect)
    ###Startmenu 
    if menu_state == "start":
        #draw start screen buttons
        menu_start()
        if menu_state == "background":
            menu_background()

    #check if the game is paused
    if game_paused == True:
    #check menustate
        menu_pause()

        # #check if Game is Over
        # if game_over == True:
        #     menu_gameOver()

    pygame.display.flip()
pygame.quit()
sys.exit()
Settings.py

Code: Alles auswählen

import pygame

#FensterGenerierung
WIDTH = 480*2.5 #1200
HEIGHT = 360*2.5 #900
FPS = 60
#Variablen
running = True

#Farbgrundsetting
RED = (255,0,0)
GREEN = (0,255,0)
BLUE = (0,0,255)

# pygame Init
pygame.init()
pygame.font.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE) 
pygame.display.set_caption("Menu")#Titel
clock = pygame.time.Clock()
Menu.py

Code: Alles auswählen

import pygame, sys, os
import settings



#button class
class Button():
	def __init__(self, x, y, image, scale):
		width = image.get_width()
		height = image.get_height()
		self.image = pygame.transform.scale(image, (int(width * scale), int(height * scale)))
		self.rect = self.image.get_rect()
		self.rect.topleft = (x, y)
		self.clicked = False

	def draw(self, surface):
		action = False
		#get mouse position
		pos = pygame.mouse.get_pos()

		#check mouseover and clicked conditions
		if self.rect.collidepoint(pos):
			if pygame.mouse.get_pressed()[0] == 1 and self.clicked == False:
				self.clicked = True
				action = True

		if pygame.mouse.get_pressed()[0] == 0:
			self.clicked = False

		#draw button on screen
		surface.blit(self.image, (self.rect.x, self.rect.y))

		return action


game_over = False
game_paused = False
menu_state = "start"


#Defintion Schrift
font = pygame.font.SysFont("arialblack",40)

#Definition Schriffarb
TEXT_COL = (255,255,255)

def draw_text(text, font, text_col, x, y):
    img =font.render(text,True,text_col)
    settings.screen.blit(img,(x,y))


###Verzeichnis der Datei
game_folder = os.path.dirname(__file__)
button_folder = os.path.join(game_folder, 'img/Buttons')
###Laden der Buttons
sPVP_img = pygame.image.load(os.path.join(button_folder, 'Button_Start_PvP.png')).convert_alpha()
sPVE_img = pygame.image.load(os.path.join(button_folder, 'Button_Start_PvE.png')).convert_alpha()
load_img = pygame.image.load(os.path.join(button_folder, 'Button_Load.png')).convert_alpha()
exit_img = pygame.image.load(os.path.join(button_folder, 'Button_Exit.png')).convert_alpha()
back_img = pygame.image.load(os.path.join(button_folder, 'Button_back.png')).convert_alpha()
start_img = pygame.image.load(os.path.join(button_folder, 'Button_Start_Game.png')).convert_alpha()
backg1_img = pygame.image.load(os.path.join(button_folder, 'mountain.png')).convert_alpha()
backg2_img = pygame.image.load(os.path.join(button_folder, 'field.png')).convert_alpha()
backg3_img = pygame.image.load(os.path.join(button_folder, 'clouds.png')).convert_alpha()

#Instanzierung der Button
sPVP_button = Button(504, 125, sPVP_img, 1)
sPVE_button = Button(504, 250, sPVE_img, 1)
load_button = Button(504, 375, load_img, 1)
exit_button = Button(504, 625, exit_img, 1)
back_button = Button(504, 500, back_img, 1)
backg1_button = Button(104, 375, backg1_img, 1)
backg2_button = Button(304, 375, backg2_img, 1)
backg3_button = Button(504, 375, backg3_img, 1)

def menu_start():
        if sPVP_button.draw(settings.screen):
            print("StartPvP")
            menu_state = "background" 
            menu_background() #ruft zwar auf, allerdings nur kurz
        if sPVE_button.draw(settings.screen):
            print("Start PVE")
            menu_state = "background"
            menu_background()
        if load_button.draw(settings.screen):
            print("Show Savings")
        if highscore_button.draw(settings.screen):
            print("Show Highscore")
        if exit_button.draw(settings.screen):
            print("Exit Main")
            settings.running = False

def menu_background():
        
        draw_text("Choose your Background", font, TEXT_COL,120,300)
        if backg1_button.draw(settings.screen):
            print("Start Mountain")
        if backg2_button.draw(settings.screen):
            print("Start Field")
        if backg3_button.draw(settings.screen):
            print("Start Cloud")
        if back_button.draw(settings.screen):
            sleep(0.5)
            menu_state ="start"
            
Klicke ich nun auf den Button "Start PvP" scheint das background-Menü zwar kurz aufgerufen zu werden, allerdings verschwindet es sofort wieder und ich bin im Start-menü.
Wo ist mein Gedankenfehler in der main.py beziehungsweise was mache ich in der menu.py falsch?

Folgende Varianten habe ich bereits ausprobiert. Da es allerdings auch nicht zum Ziel führt, muss es ein grundsätzlicher Fehler sein, auf den ich leider nicht komme.

main.py --> gleiche Fehlerbild

Code: Alles auswählen

    if menu_state == "start":
        #draw start screen buttons
        menu_start()
        
    if menu_state == "background":
        menu_background()
        
main.py --> gleiche Fehlerbild

Code: Alles auswählen

    if menu_state == "start":
        #draw start screen buttons
        menu_start()
        
menu.py --> keine Reaktion

Code: Alles auswählen

def menu_start():
        if sPVP_button.draw(settings.screen):
            print("StartPvP")
            menu_state = "background" # ruft nicht auf
            
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@hanseb: Du schreibst Klassen und benutzt globale Variablen. Für letzteres gibt es so überhaupt keine Ausrede mehr wenn man ein objektorientiertes Programm schreibt.

Da steht viel zu viel Code auf Modulebene. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Sternchen-Importe sind Böse™. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Der Name soll dem Leser vermitteln was der Wert dahinter im Programm bedeutet, nicht zum rätseln zwingen.

Man nummeriert keine Namen. Dann will man sich entweder bessere Namen überlegen, oder gar keine Einzelnamen/-werte verwenden, sondern eine Datenstruktur. Oft eine Liste.

Kommentare sollen dem Leser einen Mehrwert über den Code geben. Faustregel: Kommentare beschreiben nicht *was* der Code macht, denn das steht da bereits als Code, sondern warum er das macht. Sofern das nicht offensichtlich ist. Offensichtlich ist in aller Regel auch was in der Dokumentation von Python und den verwendeten Bibliotheken steht.

`sys.exit()` sollte man nur verwenden wenn man mindestens potentiell einen anderen Rückgabecode als 0 an den Aufrufer übermitteln will. Als letzte Anweisung in einem Programm macht es ohne Argument überhaupt keinen Sinn, denn da ist das Programm ja sowieso zuende und es passiert das gleiche als wenn der Aufruf da nicht stehen würde.

`os.path` & Co verwendet man seit dem es `pathlib` gibt nicht mehr in neuen Programmen. Es macht nicht wirklich Sinn `os.path.join()` zu verwenden und in den Bestandteilen dann doch wieder hart kodierte Pfadtrenner stehen zu haben.

In `pygame.init()` steckt die Initialisierung der Untermodule mit drin. Eine erneute Initialisierung von `pygame.font` ist also überflüssig.

Bei `menu_state` sollte man auch ``elif`` verwenden, denn das Menü kann ja immer nur in einem Zustand sein. Und ans Ende würde ich ein ``else`` setzen, für den Fall, das man sich mal irgendwo vertippt hat und ein ungültiger Wert in `menu_state` steht. Zum Beispiel mit einem ``assert False``. Damit das Programm in so einem Fall dann nicht in einer Endlosschleife steckt und nicht mehr reagiert, man aber nicht weiss warum.

Statt Zeichenketten, böte sich auch ein Aufzählungstyp an den man mit `enum.Enum` erstellen kann, oder man verwendet gar keine Indirektion, sondern drück den Zustand durch Funktionen oder Methoden aus, die den Zustand auch gleich umsetzen. Spart dann auch die ``if``/``elif``-Kaskade.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
hanseb
User
Beiträge: 2
Registriert: Montag 29. Mai 2023, 21:20

Moin,

besten Dank für die schnelle Antwort. Die angesprochenen Hinweise werde ich einarbeiten. Soweit ich es verstanden habe, löse ich damit aber nicht mein Problem mit dem Aufruf der Funktion background() über die Buttons Start PvP und Start PvE .

Gruss
hanseb
Benutzeravatar
__blackjack__
User
Beiträge: 13111
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@hanseb: Das Problem ist egal solange da noch globale Variablen existieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten