#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2013
    Posts
    5
    Rep Power
    0

    Pygame Drawing Rectangles


    Hi, I've been trying to program my GUI for my Computing project.
    More specifically I have been trying to draw a rectangle, for the ultimate goal of creating a button for the navigation menu.

    My code is as below:

    Code:
    ###########################
    # Import libraries
    ###########################
    import pygame, sys #import pygame and sys modules
    from pygame.locals import * #import all from local pygame
    ###########################
    # Initialise Pygame
    ###########################
    pygame.init() #initialise pygame
    ###########################
    # Directories for Images
    ###########################
    backgroundImage = "bg3.jpg" #image for background
    iconImage = "icon.png" #image for icon
    ###########################
    # Load and Convert Images
    ###########################
    screen=pygame.display.set_mode((1200,720),0,32) #new display, called screen, 1200x720, 32 bit colour
    canvasBackground=pygame.image.load(backgroundImage).convert() #set background to load and convert "bif"
    mainIcon = pygame.image.load(iconImage).convert_alpha() #convert icon to alpha (transparency) and set to icon1
    ###########################
    # Create Lines Function
    ###########################
    def create_lines(startx,starty,colour,howmany,label1,label2,label3,label4,label5):
        ###########################
        # Navigation Menu Text
        ###########################
        textList = []
        textList.append(label1)
        textList.append(label2)
        textList.append(label3)
        textList.append(label4)
        textList.append(label5)
        ###########################
        # For Loop Settings
        ###########################
        x1 = startx
        y1 = starty
        x2 = x1
        y2 = y1 + 80
        x3 = x1 + 10
        y3 = y2 + 10
        x4 = x1 + 300
        y4 = y3
        yLine = 50
        line_colour = colour
        ###########################
        # Navigation Text and Lines Loop
        ###########################
        for i in range(howmany):
            ###########################
            # Drawing Lines
            ###########################
            pygame.draw.lines(screen, (colour), False, [(x1,y1), (x2,y2), (x3,y3),(x4,y4)], 1)
            ###########################
            # Adding Text
            ###########################
            tempText = textList[i]
            label=mainFont.render(tempText,1,(255,255,255))
            screen.blit(label,(930,yLine))
            ###########################
            # Variable Addition
            ###########################
            yLine = yLine + 115
            y1 = y1 + 115
            y2 = y2 + 115
            y3 = y3 + 115
            y4 = y4 + 115
    
    ###########################
    # Pygame While Loop
    ###########################
    while True: #run while loop all of the time
        for event in pygame.event.get(): #every event that occurs
            if event.type == QUIT: #if QUIT is pressed
                pygame.quit() #quit pygame
                sys.exit() #quit application
    
        ###########################
        # Caption and Icon Settings
        ###########################
        pygame.display.set_caption("SynSim - Main Menu") #set pygame caption
        pygame.display.set_icon(mainIcon) #set icone image to value of icon1
        ###########################
        # Font Settings
        ###########################
        mainTitleFont = pygame.font.Font("BODONI.ttf", 80) #set myfont1 to Bodoni MT - size 80px
        mainFont = pygame.font.Font("BODONI.ttf", 15) #set myfont2 to Bodoni MT - size 15px
        ###########################
        # Text Render
        ###########################
        mainTitle = mainTitleFont.render("SynSim", 1, (255,255,255)) #set label to render myfont1, for title, colour=white
        description1 = mainFont.render("Visual representations of the inner workings of synapses, ",1,(255,255,255)) #render myfont2 description line, colour=white
        description2 = mainFont.render("produced in accordance with the Edexcel GCSE Biology syllabus.", 1, (255,255,255)) #render myfont2 description line, colour=white
        description3 = mainFont.render("Including explanations including examples of the effects of drugs.",1,(255,255,255)) #render myfont2 description line, colour=white
        ###########################
        # Screen Blitting
        ###########################
        screen.blit(canvasBackground, (0,0)) #display background to screen, topleft corner at (0,0)
        screen.blit(mainTitle, (25, 25)) #display label (title) to screen, topleft corner at (25,25)
        screen.blit(description1, (10,660)) #display description line to screen, topleft corner at (10,660)
        screen.blit(description2, (10,680)) #display description line to screen, topleft corner at (10,680)
        screen.blit(description3, (10,700)) #display description line to screen, topleft corner at (10,700)
        ###########################
        # Navigation Menu
        ###########################
        create_lines(880,10,(255,255,255),5,"What is a Synapse?","Drug Effects of Stimulants","Drug EffectsDepressants","Exam Questions","Quit")
        pygame.draw.rect(screen, (140,240,130), Rect((100,100),(130,1)))
        ###########################
        # Update Pygame
        ###########################
        pygame.display.update() #update display
    Near the end of the program, is where I'm trying to draw the rectangle. I believe the syntax and the code is right - I thought it might be where I've physically placed it in the program.

    Thanks,
    acollinge
  2. #2
  3. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    194
    Rep Power
    2
    Ok... well...
    The first thing I want to say is... this is a really, really good example of how NOT to comment code. Your commenting style makes the code so unreadable that the first thing I have to do is remove them all. Also, get rid of the star import; really; seriously; don't listen to anyone who says otherwise. You should also always have a clock to restrict the framerate of your program; otherwise you burn cpu needlessly.

    On to specifics... Forget about pygame.draw.rect. Use Surface.fill instead (also I'm not sure why you are trying to draw a rectangle with a height of only 1 pixel).

    I took out your resources as I don't have them, but take a look (the rect is the small green block below your main title):
    python Code:
    import sys
    import os
    import pygame as pg
     
    os.environ["SDL_VIDEO_CENTERED"] = '1'
    pg.init() #initialise pg
    screen=pg.display.set_mode((1200,720))
    clock = pg.time.Clock()
     
     
    def create_lines(startx,starty,colour,howmany,label1,label2,label3,label4,label5):
     
        textList = []
        textList.append(label1)
        textList.append(label2)
        textList.append(label3)
        textList.append(label4)
        textList.append(label5)
     
        x1 = startx
        y1 = starty
        x2 = x1
        y2 = y1 + 80
        x3 = x1 + 10
        y3 = y2 + 10
        x4 = x1 + 300
        y4 = y3
        yLine = 50
        line_colour = colour
     
        for i in range(howmany):
     
            pg.draw.lines(screen, (colour), False, [(x1,y1), (x2,y2), (x3,y3),(x4,y4)], 1)
     
            tempText = textList[i]
            label=mainFont.render(tempText,1,(255,255,255))
            screen.blit(label,(930,yLine))
     
            yLine = yLine + 115
            y1 = y1 + 115
            y2 = y2 + 115
            y3 = y3 + 115
            y4 = y4 + 115
     
     
    while True: #run while loop all of the time
        for event in pg.event.get(): #every event that occurs
            if event.type == pg.QUIT: #if QUIT is pressed
                pg.quit() #quit pg
                sys.exit() #quit application
     
     
        pg.display.set_caption("SynSim - Main Menu") #set pg caption
     
        mainTitleFont = pg.font.Font(None, 80) #set myfont1 to Bodoni MT - size 80px
        mainFont = pg.font.Font(None, 15) #set myfont2 to Bodoni MT - size 15px
     
        mainTitle = mainTitleFont.render("SynSim", 1, (255,255,255)) #set label to render myfont1, for title, colour=white
        description1 = mainFont.render("Visual representations of the inner workings of synapses, ",1,(255,255,255)) #render myfont2 description line, colour=white
        description2 = mainFont.render("produced in accordance with the Edexcel GCSE Biology syllabus.", 1, (255,255,255)) #render myfont2 description line, colour=white
        description3 = mainFont.render("Including explanations including examples of the effects of drugs.",1,(255,255,255)) #render myfont2 description line, colour=white
     
        screen.fill((100,100,100)) #display background to screen, topleft corner at (0,0)
        screen.blit(mainTitle, (25, 25)) #display label (title) to screen, topleft corner at (25,25)
        screen.blit(description1, (10,660)) #display description line to screen, topleft corner at (10,660)
        screen.blit(description2, (10,680)) #display description line to screen, topleft corner at (10,680)
        screen.blit(description3, (10,700)) #display description line to screen, topleft corner at (10,700)
     
        create_lines(880,10,(255,255,255),5,"What is a Synapse?","Drug Effects of Stimulants","Drug EffectsDepressants","Exam Questions","Quit")
        screen.fill((140,240,130),(100,100,130,10))
     
        clock.tick(60.0)
        pg.display.update() #update display

    Feel free to inquire further if you need assistance.

    -Mek

    Edit:
    Oh yeah, also. I didn't make the change here, but you should load your font objects once. Not every time through your loop. If your program becomes resource intensive you will need to address this.

    Edit2:
    Here is a very basic example that shows how to set up a pygame program in an organized fashion. Click and drag the red square.
    https://github.com/Mekire/meks-pygam...r/drag_text.py

    Comments on this post

    • b49P23TIvg agrees : with laughs.
    • Dietrich agrees : well said
    Last edited by Mekire; November 21st, 2013 at 09:52 AM.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2013
    Posts
    5
    Rep Power
    0
    Thank you.

    How could I use screen.fill, to use as a button?

    Thanks

    Originally Posted by Mekire
    Ok... well...
    The first thing I want to say is... this is a really, really good example of how NOT to comment code. Your commenting style makes the code so unreadable that the first thing I have to do is remove them all. Also, get rid of the star import; really; seriously; don't listen to anyone who says otherwise. You should also always have a clock to restrict the framerate of your program; otherwise you burn cpu needlessly.

    On to specifics... Forget about pygame.draw.rect. Use Surface.fill instead (also I'm not sure why you are trying to draw a rectangle with a height of only 1 pixel).

    I took out your resources as I don't have them, but take a look (the rect is the small green block below your main title):
    python Code:
    import sys
    import os
    import pygame as pg
     
    os.environ["SDL_VIDEO_CENTERED"] = '1'
    pg.init() #initialise pg
    screen=pg.display.set_mode((1200,720))
    clock = pg.time.Clock()
     
     
    def create_lines(startx,starty,colour,howmany,label1,label2,label3,label4,label5):
     
        textList = []
        textList.append(label1)
        textList.append(label2)
        textList.append(label3)
        textList.append(label4)
        textList.append(label5)
     
        x1 = startx
        y1 = starty
        x2 = x1
        y2 = y1 + 80
        x3 = x1 + 10
        y3 = y2 + 10
        x4 = x1 + 300
        y4 = y3
        yLine = 50
        line_colour = colour
     
        for i in range(howmany):
     
            pg.draw.lines(screen, (colour), False, [(x1,y1), (x2,y2), (x3,y3),(x4,y4)], 1)
     
            tempText = textList[i]
            label=mainFont.render(tempText,1,(255,255,255))
            screen.blit(label,(930,yLine))
     
            yLine = yLine + 115
            y1 = y1 + 115
            y2 = y2 + 115
            y3 = y3 + 115
            y4 = y4 + 115
     
     
    while True: #run while loop all of the time
        for event in pg.event.get(): #every event that occurs
            if event.type == pg.QUIT: #if QUIT is pressed
                pg.quit() #quit pg
                sys.exit() #quit application
     
     
        pg.display.set_caption("SynSim - Main Menu") #set pg caption
     
        mainTitleFont = pg.font.Font(None, 80) #set myfont1 to Bodoni MT - size 80px
        mainFont = pg.font.Font(None, 15) #set myfont2 to Bodoni MT - size 15px
     
        mainTitle = mainTitleFont.render("SynSim", 1, (255,255,255)) #set label to render myfont1, for title, colour=white
        description1 = mainFont.render("Visual representations of the inner workings of synapses, ",1,(255,255,255)) #render myfont2 description line, colour=white
        description2 = mainFont.render("produced in accordance with the Edexcel GCSE Biology syllabus.", 1, (255,255,255)) #render myfont2 description line, colour=white
        description3 = mainFont.render("Including explanations including examples of the effects of drugs.",1,(255,255,255)) #render myfont2 description line, colour=white
     
        screen.fill((100,100,100)) #display background to screen, topleft corner at (0,0)
        screen.blit(mainTitle, (25, 25)) #display label (title) to screen, topleft corner at (25,25)
        screen.blit(description1, (10,660)) #display description line to screen, topleft corner at (10,660)
        screen.blit(description2, (10,680)) #display description line to screen, topleft corner at (10,680)
        screen.blit(description3, (10,700)) #display description line to screen, topleft corner at (10,700)
     
        create_lines(880,10,(255,255,255),5,"What is a Synapse?","Drug Effects of Stimulants","Drug EffectsDepressants","Exam Questions","Quit")
        screen.fill((140,240,130),(100,100,130,10))
     
        clock.tick(60.0)
        pg.display.update() #update display

    Feel free to inquire further if you need assistance.

    -Mek

    Edit:
    Oh yeah, also. I didn't make the change here, but you should load your font objects once. Not every time through your loop. If your program becomes resource intensive you will need to address this.

    Edit2:
    Here is a very basic example that shows how to set up a pygame program in an organized fashion. Click and drag the red square.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    194
    Rep Power
    2
    Originally Posted by acollinge
    How could I use screen.fill, to use as a button
    Well, if you just want a simple way to make something happen when you click an area of the screen you can use a pygame.Rect (note that this is not the same as pygame.draw.rect) and then use Rect.collidepoint to determine if it is clicked.

    The simplest example would be:
    python Code:
    button_rect = pygame.Rect(100,100,100,50)
    Then in your event loop:
    python Code:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if button_rect.collidepoint(event.pos):
                #run some function
    And in your draw phase have:
    python Code:
    screen.fill(button_color,button_rect)
    Here is a full example of the simple method (click the rectangle to change screen color):
    python Code:
    import os
    import sys
    import random
    import pygame as pg
     
     
    class Control(object):
        def __init__(self):
            os.environ["SDL_VIDEO_CENTERED"] = '1'
            pg.init()
            self.screen = pg.display.set_mode((700,500))
            self.screen_rect = self.screen.get_rect()
            self.clock = pg.time.Clock()
            self.done = False
            self.fps = 60.0
            self.color = (255,255,255)
            self.button_rect = pg.Rect(100,100,100,50)
            self.button_color = (255,0,0)
     
        def event_loop(self):
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    self.done = True
                elif event.type == pg.MOUSEBUTTONDOWN:
                    if self.button_rect.collidepoint(event.pos):
                        self.change_color()
     
        def change_color(self):
            self.color = [random.randint(0,255) for _ in range(3)]
     
        def main_loop(self):
            while not self.done:
                self.event_loop()
                self.screen.fill(self.color)
                self.screen.fill(self.button_color,self.button_rect)
                pg.display.update()
                self.clock.tick(self.fps)
     
     
    if __name__ == "__main__":
        run_it = Control()
        run_it.main_loop()
        pg.quit()
        sys.exit()

    If you want more sophisticated buttons however, and you anticipate needing a number of them, you are going to want to use a button class.

    Here is an example (though I may have gone a bit overboard):
    python Code:
    import os
    import sys
    import random
    import pygame as pg
     
     
    RED = (255,0,0)
    BLUE = (0,0,255)
    GREEN = (0,255,0)
    BLACK = (0,0,0)
    WHITE = (255,255,255)
     
     
    class Button(object):
        def __init__(self,rect,color,function,**kwargs):
            self.rect = pg.Rect(rect)
            self.color = color
            self.function = function
            self.clicked = False
            self.process_kwargs(kwargs)
            self.render_text()
     
        def process_kwargs(self,kwargs):
            """Various optional customization you can change by passing kwargs."""
            settings = {"hover_color" : None,
                        "clicked_color" : None,
                        "on_release" : True,
                        "text" : None,
                        "font" : pg.font.Font(None,16),
                        "font_color" : WHITE}
            for kwarg in kwargs:
                if kwarg in settings:
                    settings[kwarg] = kwargs[kwarg]
                else:
                    raise AttributeError("Button has no keyword: {}".format(kwarg))
            self.__dict__.update(settings)
     
        def render_text(self):
            if self.text:
                self.text = self.font.render(self.text,True,self.font_color)
     
        def check_event(self,event):
            """The button needs to be passed events from your program event loop."""
            if event.type == pg.MOUSEBUTTONDOWN and event.button == 1:
                if self.rect.collidepoint(event.pos):
                    self.clicked = True
                    if not self.on_release:
                        self.function()
            elif event.type == pg.MOUSEBUTTONUP and event.button == 1:
                if self.clicked and self.on_release:
                    self.function()
                self.clicked = False
     
        def update(self,surface):
            """Update needs to be called every frame in the main loop."""
            color = self.color
            if self.clicked and self.clicked_color:
                color = self.clicked_color
            elif self.hover_color and self.rect.collidepoint(pg.mouse.get_pos()):
                color = self.hover_color
            surface.fill(BLACK,self.rect)
            surface.fill(color,self.rect.inflate(-4,-4))
            if self.text:
                text_rect = self.text.get_rect(center=self.rect.center)
                surface.blit(self.text,text_rect)
     
     
    class Control(object):
        def __init__(self):
            os.environ["SDL_VIDEO_CENTERED"] = '1'
            pg.init()
            self.screen = pg.display.set_mode((700,500))
            self.screen_rect = self.screen.get_rect()
            self.clock = pg.time.Clock()
            self.done = False
            self.fps = 60.0
            self.color = WHITE
            msg = "Change the screen color."
            self.button = Button((0,0,200,50),RED,self.change_color,text=msg,
                                 hover_color=BLUE,clicked_color=GREEN)
            self.button.rect.center = (self.screen_rect.centerx,100)
     
        def change_color(self):
            self.color = [random.randint(0,255) for _ in range(3)]
     
        def event_loop(self):
            for event in pg.event.get():
                if event.type == pg.QUIT:
                    self.done = True
                self.button.check_event(event)
     
        def main_loop(self):
            while not self.done:
                self.event_loop()
                self.screen.fill(self.color)
                self.button.update(self.screen)
                pg.display.update()
                self.clock.tick(self.fps)
     
     
    if __name__ == "__main__":
        run_it = Control()
        run_it.main_loop()
        pg.quit()
        sys.exit()
    With this you could make any number of buttons and place them in any location linked to any function.

    Note that I haven't thoroughly tested the above but if you feel like making the leap to object orientation, it may help.
    -Mek
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2013
    Posts
    5
    Rep Power
    0
    Sorted. Cheers.

IMN logo majestic logo threadwatch logo seochat tools logo