Pygame simulate scroll menu

Pygame simulate scroll menu

Postby metulburr » Sun Apr 28, 2013 2:52 am

This would be my first guess at creating a scroll menu system in pygame. Create a menu rect, with a scrollbar rect,where the the scrollbar rect literally moves up an down the menu based on size of menu inserts. Is that the basic setup of a scroll menu? Other than that, i am not sure of other options.

This is the code i have so far, but i havent implemented anything regarding the scrollbar yet, just the background menu rect.
Code: Select all
import pygame

class Control:
   def __init__(self):
      self.screensize = (800,600)
      self.screen = pygame.display.set_mode(self.screensize)
      self.clock = pygame.time.Clock()
      self.gamestate = True
      self.set_menu()
   
      self.mainloop()
      
   def set_menu(self):
      menu_width = self.screensize[0] // 4
      table_size = self.screensize[0] - menu_width
      menu_top = 0
      menu_height = self.screensize[1]
      self.menu = pygame.Rect(table_size, menu_top, menu_width, menu_height)
      self.menu_bg = (50,50,50)
      
   def mainloop(self):
      while self.gamestate:
         for event in pygame.event.get():
            if event.type == pygame.QUIT:
               self.gamestate = False
         
         self.update()
         pygame.display.flip()
         
   def update(self):
      self.screen.fill((100,100,100))
      pygame.draw.rect(self.screen, self.menu_bg, self.menu, 0)

app = Control()


I dont know. For me, (sleepy + coding) == (bad coding + frustration). So maybe i should jsut go to bed and play with this in the morning
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby Mekire » Sun Apr 28, 2013 3:48 am

Well I implemented scrolling directory menus in one of my older projects. The code is fairly hellish though so I don't know if it would be worth your effort.

If you are interested you can find it here:
Life as a Bit

I will try and write up a clean reusable class when I get a chance.

-Mek
User avatar
Mekire
 
Posts: 1010
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Pygame simulate scroll menu

Postby metulburr » Sun Apr 28, 2013 9:15 am

Well I implemented scrolling directory menus in one of my older projects. The code is fairly hellish though so I don't know if it would be worth your effort.

Yeah that is what initially made me think about it. But i could definetely tell it was one of your older projects.

This is all i found regarding scroll bars in your code, but i am not sure if ther eis enough to figure out what is going on/.
Code: Select all
    def scroll_bar(self,content):
        #scroll bars should really be a seperate usable class.  This would cut down on redundant code.
        bartotal = 120
        if len(content) > 12:
            barsize  = 120*(12/float(len(content)))
            perindex = 120/float(len(content))
        else:
            barsize  = 120
            perindex = 10
        if barsize < 10:
            barsize = 10
        return barsize,perindex



and then this too
Code: Select all
                        #scroll up and down stamp selection directory (why here? why not?)
                        if   367 < self.target[1] < 379:
                            if self.dirindex > 0:
                                self.dirindex -= 1
                            elif len(self.dirlist)>12:
                                self.dirindex = (len(self.dirlist)-12)
                        elif 499 < self.target[1] < 511:
                            if self.dirindex < len(self.dirlist)-12:
                                self.dirindex += 1
                            else:
                                self.dirindex = 0
                        elif 379 < self.target[1] < 499:
                            barplace = self.dirindex*self.stampindsize
                            if barplace+self.stampscroll > 120:
                                barplace = 120-self.stampscroll
                            if   379+barplace > self.target[1]:
                                if self.dirindex > 12: self.dirindex -= 12
                                else: self.dirindex = 0
                            elif 379+barplace+self.stampscroll < self.target[1]:
                                if self.dirindex < len(self.dirlist)-24: self.dirindex += 12
                                else: self.dirindex = len(self.dirlist)-12
                            elif 379+barplace < self.target[1] < 379+barplace+self.stampscroll:
                                if len(self.dirlist)>12:
                                    self.stampdrag = True
                                    self.lastmouse = self.target[1]-(379+self.dirindex*self.stampindsize)


but there are so many magic numbers in there i am not sure what is going on
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby metulburr » Sun Apr 28, 2013 9:57 am

so i think i might of came up with a template. Where the top and height of self.menu_scroll_bar where currently is hardcoded, but depending on its varrying size, would implement such a scroll bar. Or at least i think it would.

Code: Select all
import pygame

class Control:
   def __init__(self):
      self.screensize = (800,600)
      self.screen = pygame.display.set_mode(self.screensize)
      self.clock = pygame.time.Clock()
      self.gamestate = True
      self.set_menu()
   
      self.mainloop()
      
   def set_menu(self):
      menu_width = self.screensize[0] // 4
      table_size = self.screensize[0] - menu_width
      
      menu_top = 0
      menu_height = self.screensize[1]
      self.menu = pygame.Rect(table_size, menu_top, menu_width, menu_height)
      self.menu_bg = (50,50,50)
      
      scrollbar_width = 10
      self.menu_scroll_bar_bg = pygame.Rect(self.screensize[0] - scrollbar_width, menu_top, scrollbar_width, self.screensize[1])
      
      top = 20
      height = 20
      self.menu_scroll_bar = pygame.Rect(self.screensize[0] - scrollbar_width, top, scrollbar_width, height)
      
   def mainloop(self):
      while self.gamestate:
         for event in pygame.event.get():
            if event.type == pygame.QUIT:
               self.gamestate = False
         
         self.update()
         pygame.display.flip()
         
   def update(self):
      self.screen.fill((100,100,100))
      pygame.draw.rect(self.screen, self.menu_bg, self.menu, 0)
      pygame.draw.rect(self.screen, (200,200,200), self.menu_scroll_bar_bg, 0)
      pygame.draw.rect(self.screen, (0,0,0), self.menu_scroll_bar, 0)
      

app = Control()
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby metulburr » Sun Apr 28, 2013 2:56 pm

now i am thinking, should i draw the menu down below the screen hieght, and represent the scroll bar with the screen in its position relevent to current position, or instead to move the scrollbar, and draw the next phase of the screen in porportion to the scrollbar's position.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby metulburr » Sun Apr 28, 2013 3:32 pm

well i went with the second one, and implemented somewhat of the feature. However i didnt utilize the Rect's center feature, which i guess would make it more realistic. I dont know why i never first think of it.

but now i am stuck at testing the scroll bar and improvising it as i need shapes to take up space in the menu and go below the screen, to be able to "use" the scroll bar in the manner intended. So now my focus shifted to somehow creating centered images in the menu that is somehow attached to the scroll bar. Where images are 2 images width, and on down the menu for as many images there are.
Code: Select all
import pygame

class Control:
   def __init__(self):
      self.screensize = (800,600)
      self.screen = pygame.display.set_mode(self.screensize)
      self.clock = pygame.time.Clock()
      self.gamestate = True
      self.click = False
      self.clickhold = False
      self.menu_scroll_bar_follow_mouse = False
      self.set_menu()

      self.mainloop()
    
   def set_menu(self):
      menu_width = self.screensize[0] // 4
      table_size = self.screensize[0] - menu_width

      menu_top = 0
      menu_height = self.screensize[1]
      self.menu = pygame.Rect(table_size, menu_top, menu_width, menu_height)
      self.menu_bg = (50,50,50)

      self.scroll_bar_width = 10
      self.menu_scroll_bar_bg = pygame.Rect(self.screensize[0] - self.scroll_bar_width, menu_top, self.scroll_bar_width, self.screensize[1])

      self.scroll_bar_top = 20
      self.scroll_bar_height = 20
      self.menu_scroll_bar = pygame.Rect(self.screensize[0] - self.scroll_bar_width, self.scroll_bar_top, self.scroll_bar_width, self.scroll_bar_height)
    
   def mainloop(self):
      while self.gamestate:
         for event in pygame.event.get():
            if event.type == pygame.QUIT:
               self.gamestate = False
            if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
               self.click = True
            if event.type == pygame.MOUSEBUTTONUP and event.button == 1:
               self.click = False
               self.clickhold = False
               self.menu_scroll_bar_follow_mouse = False
      
         self.update()
         pygame.display.flip()
         
   def mouse_click(self, rect):
      #if mouse over rect
      if rect.collidepoint(pygame.mouse.get_pos()):
         if self.click:
            self.clickhold = True
            self.menu_scroll_bar_follow_mouse = True

         
      
   def update(self):
      self.screen.fill((100,100,100))
   
         
      self.mouse_click(self.menu_scroll_bar)
      if self.menu_scroll_bar_follow_mouse:
         self.scroll_bar_top =  pygame.mouse.get_pos()[1] - 2#(self.scroll_bar_top - self.scroll_bar_height)
         self.menu_scroll_bar = pygame.Rect(self.screensize[0] - self.scroll_bar_width, self.scroll_bar_top, self.scroll_bar_width, self.scroll_bar_height)
            
      self.menu_scroll_bar.clamp_ip(self.menu_scroll_bar_bg) #stop bar from moving out of scrollb ar background
      pygame.draw.rect(self.screen, self.menu_bg, self.menu, 0)
      pygame.draw.rect(self.screen, (200,200,200), self.menu_scroll_bar_bg, 0)
      pygame.draw.rect(self.screen, (0,0,0), self.menu_scroll_bar, 0)
    

app = Control()
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby Mekire » Mon Apr 29, 2013 5:29 pm

Trying to make as generic a scrolling menu as possible. It can be placed anywhere on the screen and have any dimensions you like (given as a rect argument). Currently only for text though.

It is getting a bit out of control and needs to be cleaned up a lot but here's what I have so far.
Throw it in a folder with a lot of files and run it as main to see it in action:
Code: Select all
import pygame as pg
import sys,os

class ScrollWindow:
    def __init__(self,rect,content,font,background=None,highlight=True):
        self.rect = pg.Rect(rect)
        self.content = content
        self.font = font
        self.rendered = self.render_content((0,0,0))
        self.highlight = highlight

        if background:
            self.background = background
        else:
            self.background = pg.Surface(self.rect.size).convert()
            self.background.fill(-1)
        self.image = pg.Surface(self.rect.size).convert()

        self.gen_bar_details()
        but_size = (self.rendered[0].get_height(),)*2
        bar_r = pg.Rect(self.rect.right-but_size[0],self.rect.y,but_size[0],self.rect.height)
        self.Bar = ScrollBar(bar_r,but_size,self.options_per_page,len(self.content))

    def render_content(self,color):
        render_list = []
        for item in self.content:
            render_list.append(self.font.render(item,1,color))
        return render_list

    def activate_highlight(self):
        if self.highlight and not self.Bar.drag:
            mouse = pg.mouse.get_pos()
            relative_mouse = mouse[0]-self.rect.x,mouse[1]-self.rect.y
            if self.rect.collidepoint(mouse) and not self.Bar.rect.collidepoint(mouse):
                self.highlight_index = self.index+(relative_mouse[1]//self.height_per_option)
            else:
                self.highlight_index = None

    def gen_bar_details(self):
        self.index = 0
        self.height_per_option = self.rendered[0].get_size()[1]
        self.options_per_page = self.rect.height//self.height_per_option

    def update_bar_rect(self):
        if self.Bar.drag:
            mouse_y = pg.mouse.get_pos()[1]
            next_ind = self.Bar.drag[1]+int((mouse_y-self.Bar.drag[0])//self.Bar.perindex)
            if next_ind <= 0:
                self.index = 0
            elif next_ind > len(self.content)-self.options_per_page:
                self.index = len(self.content)-self.options_per_page
            else:
                self.index = next_ind
        self.Bar.bar_rect.y = self.Bar.button_size[1]+(self.index*self.Bar.perindex)

    def update_content(self):
        for i,item in enumerate(self.rendered[self.index:self.index+self.options_per_page]):
            if self.index+i == self.highlight_index:
                temp = pg.Surface((self.rect.width,item.get_height()))
                temp.fill(0)
                temp.blit(self.font.render(content[self.index+i],1,(255,255,255)),(5,0))
                self.image.blit(temp,(0,i*self.height_per_option))
            else:
                self.image.blit(item,(5,i*self.height_per_option))

    def update(self,Surf):
        self.activate_highlight()
        self.update_bar_rect()
        self.image.blit(self.background,(0,0))
        self.update_content()
        Surf.blit(self.image,self.rect)
        self.Bar.update(Surf)

    def get_events(self,event):
        mouse = pg.mouse.get_pos()
        relative_mouse_to_bar = (mouse[0]-(self.rect.x+self.rect.width-self.Bar.rect.width),
                                 mouse[1]-self.rect.y)
        if event.type == pg.MOUSEBUTTONDOWN:
            if event.button == 1:
                if self.highlight_index != None:
                    return self.content[self.highlight_index]
                elif pg.Rect((0,0),self.Bar.rect.size).collidepoint(relative_mouse_to_bar):
                    if self.Bar.button_rects[0].collidepoint(relative_mouse_to_bar):
                        self.index -= (1 if self.index else -(len(self.content)-self.options_per_page))
                    elif self.Bar.button_rects[1].collidepoint(relative_mouse_to_bar):
                        self.index += (1 if self.index < len(self.content)-self.options_per_page else -self.index)
                    elif self.Bar.bar_rect.collidepoint(relative_mouse_to_bar):
                        self.Bar.drag = (pg.mouse.get_pos()[1],self.index)
                    else:
                        if relative_mouse_to_bar[1] < self.Bar.bar_rect.y:
                            if self.index - self.options_per_page > 0:
                                self.index -= self.options_per_page
                            else:
                                self.index = 0
                        elif relative_mouse_to_bar[1] > self.Bar.bar_rect.bottom:
                            if self.index+self.options_per_page < len(self.content)-self.options_per_page:
                                self.index += self.options_per_page
                            else:
                                self.index = len(self.content)-self.options_per_page
        elif event.type == pg.MOUSEBUTTONUP:
            self.Bar.drag = False

class ScrollBar:
    def __init__(self,rect,button_size,opts_per,length,
                 buttons=None,background=None,bar_image=None):
        self.rect = pg.Rect(rect)
        self.opts_per = opts_per
        self.length = float(length)
        self.buttons = buttons
        self.background = background
        self.bar_image = bar_image
        self.button_size = button_size
        self.button_rects = [pg.Rect((0,0),self.button_size),
                             pg.Rect((0,self.rect.height-self.button_size[1]),self.button_size)]
        self.barsize,self.perindex = self.make_bar()
        self.bar_rect = pg.Rect(0,self.button_size[1],self.button_size[0],self.barsize)
        self.drag = False
    def make_bar(self):
        total_bar_height = self.rect.height - 2*self.button_size[1]
        if self.length > self.opts_per:
            barsize  = total_bar_height*(self.opts_per/self.length)
            perindex = total_bar_height/self.length
            if barsize < 10:
                barsize = 10
        else:
            barsize = total_bar_height
            perindex = 10
        return barsize,perindex

    def make_image(self):
        temp = pg.Surface(self.rect.size).convert()
        if self.background:
            temp.blit(self.background,(0,0))
        else:
            temp.fill(0)
        if self.buttons:
            temp.blit(self.buttons[0],(0,0))
            temp.blit(self.buttons[1],(0,self.rect.height-self.button_size[1]))
        else:
            temp.fill((255,0,0),((0,0),self.button_size))
            temp.fill((255,0,0),((0,self.rect.height-self.button_size[1]),self.button_size))
        if self.bar_image:
            pass
        else:
            temp.fill((0,0,255),self.bar_rect)
            inner = self.bar_rect.inflate((-2,-2))
            temp.fill(-1,inner)
        self.image = temp

    def update(self,Surf):
        self.make_image()
        Surf.blit(self.image,self.rect)

#####
if __name__ == "__main__":
    pg.init()
    SCREEN = pg.display.set_mode((700,600))
    content = os.listdir(".")
    font = pg.font.SysFont("timesnewroman",15)
    MyBar = ScrollWindow((125,250,250,200),content,font)

    done = False
    while not done:
        keys = pg.key.get_pressed()
        for event in pg.event.get():
            if event.type == pg.QUIT or keys[pg.K_ESCAPE]:
                done = True
            menu_selection = MyBar.get_events(event)
            if menu_selection:
                print(menu_selection)
        SCREEN.fill((50,50,50))
        MyBar.update(SCREEN)
        pg.display.update()
    pg.quit();sys.exit()

-Mek
User avatar
Mekire
 
Posts: 1010
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Pygame simulate scroll menu

Postby metulburr » Mon Apr 29, 2013 5:34 pm

oh wow, nice. Im gonna have to dissect that, to find more about it. But yeah, i think once i figure this setup up well, i might be able to trranslate it to images for that alchemy scroll menu of completed elements.

I was switching form house to house yesterday, so i was using the forum as a codepad and text of what i was thinking, sort of thing, so i wouldnt forget.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby Mekire » Mon Apr 29, 2013 5:39 pm

Going to try to make it so you can optionally specify images to use as a skin. I just hate to have class __init__ functions that take ridiculous numbers of arguments. I think it could probably be made to work as an image menu pretty easily; at least in theory. Anyway... still needs a lot of work and a bit of documentation, it is very ugly at the moment.

-Mek
User avatar
Mekire
 
Posts: 1010
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Pygame simulate scroll menu

Postby Mekire » Tue Apr 30, 2013 5:05 am

Next version. Menu can be customized with numerous keyword arguments (these are best created as a dict and passed using **dict unpacking).

Unzip the contents of the zip in a folder with a lot of files and run as main to test.
http://code.google.com/p/pygame-scrollbar/downloads/list

You must pass events from your main event loop to the menus get_events method. This method will return the corresponding string if you click on a menu option.

-Mek
User avatar
Mekire
 
Posts: 1010
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Pygame simulate scroll menu

Postby metulburr » Tue Apr 30, 2013 9:08 am

nice! Sweet, documentation.

I havent even had much time to look at the previous example before you posted this one, lol. I plan on it though, going in depth in pygame, for my next hurdle to get over in python. I have been putting it off for a year, learning the basics of programming in general, python basics, math skills. I think its finally time to jump in fully to 2d game programming. Which was my ultimate goal in originally starting programming in the first place. But i got distracted so much with all the things you can do with python, lol.
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY

Re: Pygame simulate scroll menu

Postby Mekire » Tue May 07, 2013 1:49 am

Added some more to this. Now I have the scroll window class and a movable window class. I have also turned it into a package for convenience.

Just unzip anywhere and run the driver_test.py file for an example. The window can be moved around the screen by dragging the handle. The scroll window now supports mouse scroll wheel events as well.

http://code.google.com/p/pygame-scrollbar/downloads/list
-Mek
User avatar
Mekire
 
Posts: 1010
Joined: Thu Feb 07, 2013 11:33 pm
Location: Amakusa, Japan

Re: Pygame simulate scroll menu

Postby metulburr » Tue May 07, 2013 1:55 am

impressive
New Users, Read This
OS Ubuntu 14.04, Arch Linux, Gentoo, Windows 7/8
https://github.com/metulburr
steam
User avatar
metulburr
 
Posts: 1499
Joined: Thu Feb 07, 2013 4:47 pm
Location: Elmira, NY


Return to Game Development

Who is online

Users browsing this forum: No registered users and 8 guests