Hi all
With all the activity around the design of pyglet user interfaces, I tried to code a simple widget placement algorithm inspired by GTK hbox/ vbox. Currently, the class only handles horizontal placement but vertical placement is straightforward. Screenshot : http://www.loria.fr/~rougier/tmp/hbox.png Code: http://www.loria.fr/~rougier/tmp/hbox.py On the screenshot, there are 15 different placement using the exact same 3 widgets. The main idea is to use spacers (a dumb widget) that can be inserted anywhere between widgets. Those spacers can be expandable or have a fixed size. The same goes for any widget and here you have plenty of placement freedom with a single line, both fully automatic placement & sizing and/or manual placement & sizing with pixel precision. Using both hbox and vbox should allow for easy placement of any kind of widgets. Nicolas For the archive, I paste code below: #!/usr/bin/env python # -*- coding: utf-8 -*- # ------------------------------------------------------------------------------ # Copyright (c) 2009 Nicolas Rougier # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # * Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # * Neither the name of pyglet nor the names of its contributors may be used # to endorse or promote products derived from this software without specific # prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ----------------------------------------------------------------------------- import random from operator import add class Widget(object): def __init__(self, position=(0,0), size=(-1,-1)): self.position = list(position) self.size_request = list(size) self.size_allocation = list(size) def hexpand(self): return self.size_request[0] == -1 def draw(self, x0=0, y0=0): x,y = x0+self.position[0]+.45, y0+self.position[1]+.45 w,h = self.size_allocation gl.glBegin(gl.GL_LINE_LOOP) gl.glVertex2f(x, y) gl.glVertex2f(x+w, y) gl.glVertex2f(x+w, y+h-1) gl.glVertex2f(x, y+h-1) gl.glEnd() class Spacer(Widget): ''' ''' def __init__(self, position=(0,0), size=(-1,-1)): Widget.__init__(self, position, size) def draw(self, x0=0, y0=0): pass class HBox(Widget): ''' ''' def __init__(self, position=(0,0), size=(-1,-1), homogeneous=True, spacing=3): ''' ''' Widget.__init__(self, position, size) self.homogeneous = homogeneous self.spacing = spacing self.children = [] def __call__(self,*args): self.children = [] for child in args: if isinstance(child,Widget): self.children.append(child) if isinstance(child,tuple): child,size = child if isinstance(child,Widget): child.size_request[0] = size self.children.append(child) elif isinstance(child,int): self.children.append(Spacer(size=(child,-1))) elif isinstance(child,str): self.children.append(Spacer(size=(-1,-1))) self.update_layout() return self def update_layout(self): ''' ''' width = self.size_allocation[0] # Minimum widget width (but spacers) minimum_width = 10 # Reset size allocation for all children for child in self.children: child.size_allocation[0] = 0 # All children all_children = self.children # Expandable children children_= [child for child in self.children if not isinstance (child,Spacer) and child.hexpand()] # Non-expandable spacers spacers = [child for child in self.children if isinstance (child,Spacer) and not child.hexpand()] # Non-expandable children children = [child for child in self.children if not isinstance (child,Spacer) and not child.hexpand()] # Expandable spacers # If we have expandable children, they will eat-up remaining space, no need of expandable spacers if len(children_) > 0: spacers_ = [] all_children = [child for child in self.children if not isinstance(child,Spacer) or not child.hexpand()] else: spacers_= [child for child in self.children if isinstance (child,Spacer) and child.hexpand()] # Makes non-expandable children to have same size and allocates # remaining space equally among expandable children if self.homogeneous: # Computes common size for non-expandable children free_space = width - (len(all_children)-1)*self.spacing free_space -= reduce(add,[child.size_request[0] for child in spacers] or [0]) free_space -= len(children_)*minimum_width common_size = max([child.size_request[0] for child in children] or [0]) if common_size*len(children) > free_space: common_size = free_space/len(children) # Assign common size to non-expandable children for child in children: child.size_allocation[0] = common_size else: # Computes size for non-expandable children free_space = width - (len(all_children)-1)*self.spacing free_space -= reduce(add,[child.size_request[0] for child in spacers] or [0]) children_size = max([child.size_request[0] for child in children] or [0]) scale = 1 if children_size > free_space: scale = (free_space)/float(children_size) for child in children: child.size_allocation[0] = int(child.size_request[0] *scale) # Assign fixed size to non-expandable spacers for child in spacers: child.size_allocation[0] = child.size_request[0] # Assign remaining space to expandable children if any or expandable spacers free_space = width - (len(all_children)-1)*self.spacing free_space -= reduce(add,[child.size_allocation[0] for child in spacers] or [0]) free_space -= reduce(add,[child.size_allocation[0] for child in children] or [0]) if len(children_): for child in children_: child.size_allocation[0] = int(free_space/len (children_)) elif len(spacers_): for child in spacers_: child.size_allocation[0] = int(free_space/len (spacers_)) # Assign position to children x = 0 for child in all_children: child.position[0] = x x += child.size_allocation[0] + self.spacing def draw(self, x0=0, y0=0): x,y = x0+self.position[0], y0+self.position[1] w,h = self.size_allocation gl.glColor4f(0.75, 0.75, 0.75, 1.00) gl.glBegin(gl.GL_QUADS) gl.glVertex2i(x, y) gl.glVertex2i(x+w, y) gl.glVertex2i(x+w, y+h) gl.glVertex2i(x, y+h) gl.glEnd() gl.glColor4f(0.00, 0.00, 0.00, 1.00) for child in self.children: child.draw(x0, y0) # ----------------------------------------------------------------------------- if __name__ == '__main__': import pyglet import pyglet.gl as gl window = pyglet.window.Window(width=700, height=460) def text(text, x, y): label = pyglet.text.Label(text,x=x, y=y, anchor_x = 'left', anchor_y = 'center', font_size=10, color=(0,0,0,255)) label.draw() @window.event def on_draw(): gl.glClearColor(1,1,1,1) window.clear() b1 = Widget(size=(100,20)) b2 = Widget(size=(50,20)) b3 = Widget(size=(30,20)) x = 10 y = 10 box = HBox((0,0),(400,20),False,3) # Non homogeneous box(' ',b1,b2,b3).draw(x, y+0*30), text('Right aligned', x+410, y+0*30+10) box(b1,b2,b3,' ').draw(x, y+1*30), text('Left aligned', x+410, y+1*30+10) box(' ',b1,b2,b3,' ').draw(x,y+2*30), text ('Centered', x+410, y+2*30+10) box(b1,b2,' ',b3).draw(x,y+3*30), text('Splitted (2 groups)', x+410, y+3*30+10) box(' ',b1,b2,' ',b3,' ').draw(x,y+4*30), text('Splitted (3 groups)', x+410, y+4*30+10) y = 10 + 5*30 box = HBox((0,0),(400,20),True,3) # Homogeneous box(' ',b1,b2,b3).draw(x, y+0*30), text('Homogeneous & right aligned', x+410, y+0*30+10) box(b1,b2,b3,' ').draw(x, y+1*30), text('Homogeneous & left aligned', x+410, y+1*30+10) box(' ',b1,b2,b3,' ').draw(x,y+2*30), text('Homogeneous & centered', x+410, y+2*30+10) box(b1,b2,' ',b3).draw(x,y+3*30), text('Homogeneous & splitted (2 groups)', x+410, y+3*30+10) box(' ',b1,b2,' ',b3,' ').draw(x,y+4*30), text('Homogeneous & splitted (3 groups)', x+410, y+4*30+10) y = 10 + 10*30 box = HBox((0,0),(400,20),False,0) # Manual placement box(' ', b1,2,b2,2,b3).draw(x, y+0*30), text('Manual separation & right aligned', x+410, y+0*30+10) box(b1,2,b2,2,b3,' ').draw(x, y+1*30), text('Manual separation & left aligned', x+410, y+1*30+10) box(' ',b1,2,b2,2,b3,' ').draw(x, y+2*30),text('Manual separation & centered', x+410, y+2*30+10) y = 10 + 13*30 box = HBox((0,0),(400,20),True,3) # Expandable widget with homogeneous size for others box(b1,(b2,-1),b3).draw(x, y+0*30), text('Homogenous with one expandable widget', x+410, y+0*30+10) box = HBox((0,0),(400,20),False,3) # Expandable widget with homogeneous size for others box(b1,(b2,-1),b3).draw(x, y+1*30), text('Non homogeneous with one expandable widget', x+410, y+1*30+10) pyglet.app.run() --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "pyglet-users" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/pyglet-users?hl=en -~----------~----~----~----~------~----~------~--~---
