|
|
|
| Author |
Message |
ynjh_jo
 Posts: 1596 Location: Malang, Indonesia
 |
Some1 asked me for this, so here it is.
| Code: | __all__=['ScrolledButtonsList']
from pandac.PandaModules import *
from direct.showbase.DirectObject import DirectObject
from direct.gui.OnscreenText import OnscreenText
from direct.gui.DirectGui import DirectFrame,DirectButton,DirectScrolledFrame,DGG
from direct.interval.IntervalGlobal import Sequence
from direct.task import Task
from direct.showbase.PythonUtil import clampScalar
from types import IntType
class ScrolledButtonsList(DirectObject):
"""
A class to display a list of selectable buttons.
It is displayed using scrollable window (DirectScrolledFrame).
"""
def __init__(self, parent=None, frameSize=(.8,1.2), buttonTextColor=(1,1,1,1),
font=None, itemScale=.045, itemTextScale=1, itemTextZ=0,
command=None, contextMenu=None, autoFocus=0,
colorChange=1, colorChangeDuration=1.0, newItemColor=(0,1,0,1),
rolloverColor=Vec4(1,.8,.2,1),
suppressMouseWheel=1, modifier='control'):
self.focusButton=None
self.command=command
self.contextMenu=contextMenu
self.autoFocus=autoFocus
self.colorChange=colorChange
self.colorChangeDuration=colorChangeDuration*.5
self.newItemColor=Vec4(*newItemColor)
self.rolloverColor=Vec4(*rolloverColor)
self.rightClickTextColors=(Vec4(0,1,0,1),Vec4(0,35,100,1))
self.font=font
if font:
self.fontHeight=font.getLineHeight()
else:
self.fontHeight=TextNode.getDefaultFont().getLineHeight()
self.fontHeight*=1.2 # let's enlarge font height a little
self.xtraSideSpace=.2*self.fontHeight
self.itemTextScale=itemTextScale
self.itemTextZ=itemTextZ
self.buttonTextColor=buttonTextColor
self.suppressMouseWheel=suppressMouseWheel
self.modifier=modifier
self.buttonsList=[]
self.numItems=0
self.__eventReceivers={}
# DirectScrolledFrame to hold items
self.itemScale=itemScale
self.itemVertSpacing=self.fontHeight*self.itemScale
self.frameWidth,self.frameHeight=frameSize
# I set canvas' Z size smaller than the frame to avoid the auto-generated vertical slider bar
self.frame = DirectScrolledFrame(
parent=parent,pos=(-self.frameWidth*.5,0,.5*self.frameHeight), relief=DGG.GROOVE,
state=DGG.NORMAL, # to create a mouse watcher region
frameSize=(0, self.frameWidth, -self.frameHeight, 0), frameColor=(0,0,0,.7),
canvasSize=(0, 0, -self.frameHeight*.5, 0), borderWidth=(0.01,0.01),
manageScrollBars=0, enableEdit=0, suppressMouse=0, sortOrder=1000 )
# the real canvas is "self.frame.getCanvas()",
# but if the frame is hidden since the beginning,
# no matter how I set the canvas Z pos, the transform would be resistant,
# so just create a new node under the canvas to be my canvas
self.canvas=self.frame.getCanvas().attachNewNode('myCanvas')
# slider background
SliderBG=DirectFrame( parent=self.frame,frameSize=(-.025,.025,-self.frameHeight,0),
frameColor=(0,0,0,.7), pos=(-.03,0,0),enableEdit=0, suppressMouse=0)
# slider thumb track
sliderTrack = DirectFrame( parent=SliderBG, relief=DGG.FLAT, #state=DGG.NORMAL,
frameColor=(1,1,1,.2), frameSize=(-.015,.015,-self.frameHeight+.01,-.01),
enableEdit=0, suppressMouse=0)
# page up
self.pageUpRegion=DirectFrame( parent=SliderBG, relief=DGG.FLAT, state=DGG.NORMAL,
frameColor=(1,.8,.2,.1), frameSize=(-.015,.015,0,0),
enableEdit=0, suppressMouse=0)
self.pageUpRegion.setAlphaScale(0)
self.pageUpRegion.bind(DGG.B1PRESS,self.__startScrollPage,[-1])
self.pageUpRegion.bind(DGG.WITHIN,self.__continueScrollUp)
self.pageUpRegion.bind(DGG.WITHOUT,self.__suspendScrollUp)
# page down
self.pageDnRegion=DirectFrame( parent=SliderBG, relief=DGG.FLAT, state=DGG.NORMAL,
frameColor=(1,.8,.2,.1), frameSize=(-.015,.015,0,0),
enableEdit=0, suppressMouse=0)
self.pageDnRegion.setAlphaScale(0)
self.pageDnRegion.bind(DGG.B1PRESS,self.__startScrollPage,[1])
self.pageDnRegion.bind(DGG.WITHIN,self.__continueScrollDn)
self.pageDnRegion.bind(DGG.WITHOUT,self.__suspendScrollDn)
self.pageUpDnSuspended=[0,0]
# slider thumb
self.vertSliderThumb=DirectButton(parent=SliderBG, relief=DGG.FLAT,
frameColor=(1,1,1,.6), frameSize=(-.015,.015,0,0),
enableEdit=0, suppressMouse=0)
self.vertSliderThumb.bind(DGG.B1PRESS,self.__startdragSliderThumb)
self.vertSliderThumb.bind(DGG.WITHIN,self.__enteringThumb)
self.vertSliderThumb.bind(DGG.WITHOUT,self.__exitingThumb)
self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
self.sliderThumbDragPrefix='draggingSliderThumb-'
# GOD & I DAMN IT !!!
# These things below don't work well if the canvas has a lot of buttons.
# So I end up checking the mouse region every frame by myself using a continuous task.
# self.accept(DGG.WITHIN+self.frame.guiId,self.__enteringFrame)
# self.accept(DGG.WITHOUT+self.frame.guiId,self.__exitingFrame)
self.isMouseInRegion=False
self.mouseOutInRegionCommand=(self.__exitingFrame,self.__enteringFrame)
taskMgr.doMethodLater(.2,self.__getFrameRegion,'getFrameRegion')
def __getFrameRegion(self,t):
for g in range(base.mouseWatcherNode.getNumGroups()):
region=base.mouseWatcherNode.getGroup(g).findRegion(self.frame.guiId)
if region!=None:
self.frameRegion=region
taskMgr.add(self.__mouseInRegionCheck,'mouseInRegionCheck')
break
def __mouseInRegionCheck(self,t):
"""
check if the mouse is within or without the scrollable frame, and
upon within or without, run the provided command
"""
if not base.mouseWatcherNode.hasMouse(): return Task.cont
m=base.mouseWatcherNode.getMouse()
bounds=self.frameRegion.getFrame()
inRegion=bounds[0]<m[0]<bounds[1] and bounds[2]<m[1]<bounds[3]
if self.isMouseInRegion==inRegion: return Task.cont
self.isMouseInRegion=inRegion
self.mouseOutInRegionCommand[inRegion]()
return Task.cont
def __startdragSliderThumb(self,m=None):
mpos=base.mouseWatcherNode.getMouse()
parentZ=self.vertSliderThumb.getParent().getZ(render2d)
sliderDragTask=taskMgr.add(self.__dragSliderThumb,'dragSliderThumb')
sliderDragTask.ZposNoffset=mpos[1]-self.vertSliderThumb.getZ(render2d)+parentZ
# sliderDragTask.mouseX=base.winList[0].getPointer(0).getX()
self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
base.buttonThrowers[0].node().setPrefix(self.sliderThumbDragPrefix)
self.acceptOnce(self.sliderThumbDragPrefix+'mouse1-up',self.__stopdragSliderThumb)
def __dragSliderThumb(self,t):
if not base.mouseWatcherNode.hasMouse():
return
mpos=base.mouseWatcherNode.getMouse()
# newY=base.winList[0].getPointer(0).getY()
self.__updateCanvasZpos((t.ZposNoffset-mpos[1])/self.canvasRatio)
# base.winList[0].movePointer(0, t.mouseX, newY)
return Task.cont
def __stopdragSliderThumb(self,m=None):
taskMgr.remove('dragSliderThumb')
self.__stopScrollPage()
base.buttonThrowers[0].node().setPrefix(self.oldPrefix)
if self.isMouseInRegion:
self.mouseOutInRegionCommand[self.isMouseInRegion]()
def __startScrollPage(self,dir,m):
self.oldPrefix=base.buttonThrowers[0].node().getPrefix()
base.buttonThrowers[0].node().setPrefix(self.sliderThumbDragPrefix)
self.acceptOnce(self.sliderThumbDragPrefix+'mouse1-up',self.__stopdragSliderThumb)
t=taskMgr.add(self.__scrollPage,'scrollPage',extraArgs=[int((dir+1)*.5),dir*.01/self.canvasRatio])
self.pageUpDnSuspended=[0,0]
def __scrollPage(self,dir,scroll):
if not self.pageUpDnSuspended[dir]:
self.__scrollCanvas(scroll)
return Task.cont
def __stopScrollPage(self,m=None):
taskMgr.remove('scrollPage')
def __suspendScrollUp(self,m=None):
self.pageUpRegion.setAlphaScale(0)
self.pageUpDnSuspended[0]=1
def __continueScrollUp(self,m=None):
if taskMgr.hasTaskNamed('dragSliderThumb'):
return
self.pageUpRegion.setAlphaScale(1)
self.pageUpDnSuspended[0]=0
def __suspendScrollDn(self,m=None):
self.pageDnRegion.setAlphaScale(0)
self.pageUpDnSuspended[1]=1
def __continueScrollDn(self,m=None):
if taskMgr.hasTaskNamed('dragSliderThumb'):
return
self.pageDnRegion.setAlphaScale(1)
self.pageUpDnSuspended[1]=0
def __suspendScrollPage(self,m=None):
self.__suspendScrollUp()
self.__suspendScrollDn()
def __enteringThumb(self,m=None):
self.vertSliderThumb['frameColor']=(1,1,1,1)
self.__suspendScrollPage()
def __exitingThumb(self,m=None):
self.vertSliderThumb['frameColor']=(1,1,1,.6)
def __scrollCanvas(self,scroll):
if self.vertSliderThumb.isHidden():
return
self.__updateCanvasZpos(self.canvas.getZ()+scroll)
def __updateCanvasZpos(self,Zpos):
newZ=clampScalar(Zpos, .0, self.canvasLen-self.frameHeight+.015)
self.canvas.setZ(newZ)
thumbZ=-newZ*self.canvasRatio
self.vertSliderThumb.setZ(thumbZ)
self.pageUpRegion['frameSize']=(-.015,.015,thumbZ-.01,-.01)
self.pageDnRegion['frameSize']=(-.015,.015,-self.frameHeight+.01,thumbZ+self.vertSliderThumb['frameSize'][2])
def __adjustCanvasLength(self,numItem):
self.canvasLen=float(numItem)*self.itemVertSpacing
self.canvasRatio=(self.frameHeight-.015)/(self.canvasLen+.01)
if self.canvasLen<=self.frameHeight-.015:
canvasZ=.0
self.vertSliderThumb.hide()
self.pageUpRegion.hide()
self.pageDnRegion.hide()
self.canvasLen=self.frameHeight-.015
else:
canvasZ=self.canvas.getZ()
self.vertSliderThumb.show()
self.pageUpRegion.show()
self.pageDnRegion.show()
self.__updateCanvasZpos(canvasZ)
self.vertSliderThumb['frameSize']=(-.015,.015,-self.frameHeight*self.canvasRatio,-.01)
thumbZ=self.vertSliderThumb.getZ()
self.pageUpRegion['frameSize']=(-.015,.015,thumbZ-.01,-.01)
self.pageDnRegion['frameSize']=(-.015,.015,-self.frameHeight+.01,thumbZ+self.vertSliderThumb['frameSize'][2])
def __acceptAndIgnoreWorldEvent(self,event,command,extraArgs=[]):
receivers=messenger.whoAccepts(event)
if receivers is None:
self.__eventReceivers[event]={}
else:
newD={}
for r in receivers:
newr=messenger._getObject(r) if type(r)==tuple else r
newD[newr]=receivers[r]
self.__eventReceivers[event]=newD
for r in self.__eventReceivers[event].keys():
r.ignore(event)
self.accept(event,command,extraArgs)
def __ignoreAndReAcceptWorldEvent(self,events):
for event in events:
self.ignore(event)
if self.__eventReceivers.has_key(event):
for r, method_xtraArgs_persist in self.__eventReceivers[event].items():
messenger.accept(event,r,*method_xtraArgs_persist)
self.__eventReceivers[event]={}
def __enteringFrame(self,m=None):
# sometimes the WITHOUT event for page down region doesn't fired,
# so directly suspend the page scrolling here
self.__suspendScrollPage()
BTprefix=base.buttonThrowers[0].node().getPrefix()
if BTprefix==self.sliderThumbDragPrefix:
return
self.inOutBTprefix=BTprefix
if self.suppressMouseWheel:
self.__acceptAndIgnoreWorldEvent(self.inOutBTprefix+'wheel_up',
command=self.__scrollCanvas, extraArgs=[-.07])
self.__acceptAndIgnoreWorldEvent(self.inOutBTprefix+'wheel_down',
command=self.__scrollCanvas, extraArgs=[.07])
else:
self.accept(self.inOutBTprefix+self.modifier+'-wheel_up',self.__scrollCanvas, [-.07])
self.accept(self.inOutBTprefix+self.modifier+'-wheel_down',self.__scrollCanvas, [.07])
print 'enteringFrame'
def __exitingFrame(self,m=None):
if not hasattr(self,'inOutBTprefix'):
return
if self.suppressMouseWheel:
self.__ignoreAndReAcceptWorldEvent( (
self.inOutBTprefix+'wheel_up',
self.inOutBTprefix+'wheel_down',
) )
else:
self.ignore(self.inOutBTprefix+self.modifier+'-wheel_up')
self.ignore(self.inOutBTprefix+self.modifier+'-wheel_down')
print 'exitingFrame'
def __setFocusButton(self,button,item):
if self.focusButton:
self.deselect()
self.focusButton=button
self.select()
if callable(self.command):
# run user command and pass the selected item, it's index, and the button
self.command(item,self.buttonsList.index(button),button)
def __rightPressed(self,button,m):
self.__isRightIn=True
# text0 : normal
# text1 : pressed
# text2 : rollover
# text3 : disabled
button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rightClickTextColors[self.focusButton==button])
button.bind(DGG.B3RELEASE,self.__rightReleased,[button])
button.bind(DGG.WITHIN,self.__rightIn,[button])
button.bind(DGG.WITHOUT,self.__rightOut,[button])
def __rightIn(self,button,m):
self.__isRightIn=True
button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rightClickTextColors[self.focusButton==button])
def __rightOut(self,button,m):
self.__isRightIn=False
button._DirectGuiBase__componentInfo['text2'][0].setColorScale(Vec4(1,1,1,1))
def __rightReleased(self,button,m):
button.unbind(DGG.B3RELEASE)
button.unbind(DGG.WITHIN)
button.unbind(DGG.WITHOUT)
button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rolloverColor)
if not self.__isRightIn:
return
if callable(self.contextMenu):
# run user command and pass the selected item, it's index, and the button
self.contextMenu(button['extraArgs'][1],self.buttonsList.index(button),button)
def deselect(self):
"""
stop highlighting item
"""
if self.focusButton:
self.focusButton['text_fg']=(1,1,1,1)
self.focusButton['frameColor']=(0,0,0,0)
def select(self,idx=None):
"""
highlight the item
"""
if idx is not None:
if not 0<=idx<self.numItems:
print 'SELECT : invalid index (%s)' %idx
return
self.focusButton=self.buttonsList[idx]
if self.focusButton:
self.focusButton['text_fg']=(.01,.01,.01,1)
self.focusButton['frameColor']=(1,.8,.2,1)
def clear(self):
"""
clear the list
"""
for c in self.buttonsList:
c.remove()
self.buttonsList=[]
self.focusButton=None
self.numItems=0
def addItem(self,text,extraArgs=None,atIndex=None):
"""
add item to the list
text : text for the button
extraArgs : the object which will be passed to user command(s)
(both command and contextMenu) when the button get clicked
atIndex : where to add the item
<None> : put item at the end of list
<integer> : put item at index <integer>
<button> : put item at <button>'s index
"""
button = DirectButton(parent=self.canvas,
scale=self.itemScale,
relief=DGG.FLAT,
frameColor=(0,0,0,0),text_scale=self.itemTextScale,
text=text, text_pos=(0,self.itemTextZ),text_fg=self.buttonTextColor,
text_font=self.font, text_align=TextNode.ALeft,
command=self.__setFocusButton,
enableEdit=0, suppressMouse=0)
l,r,b,t=button.getBounds()
# top & bottom are blindly set without knowing where exactly the baseline is,
# but this ratio fits most fonts
baseline=-self.fontHeight*.25
button['frameSize']=(l-self.xtraSideSpace,r+self.xtraSideSpace,baseline,baseline+self.fontHeight)
# Zc=NodePath(button).getBounds().getCenter()[1]-self.fontHeight*.5+.25
# # Zc=button.getCenter()[1]-self.fontHeight*.5+.25
# button['frameSize']=(l-self.xtraSideSpace,r+self.xtraSideSpace,Zc,Zc+self.fontHeight)
button['extraArgs']=[button,extraArgs]
button._DirectGuiBase__componentInfo['text2'][0].setColorScale(self.rolloverColor)
button.bind(DGG.B3PRESS,self.__rightPressed,[button])
isButton=isinstance(atIndex,DirectButton)
if isButton:
if atIndex.isEmpty():
atIndex=None
else:
index=self.buttonsList.index(atIndex)
self.buttonsList.insert(index,button)
if atIndex==None:
self.buttonsList.append(button)
index=self.numItems
elif not isButton:
index=int(atIndex)
self.buttonsList.insert(index,button)
Zpos=(-.7-index)*self.itemVertSpacing
button.setPos(.02,0,Zpos)
if index!=self.numItems:
for i in range(index+1,self.numItems+1):
self.buttonsList[i].setZ(self.buttonsList[i],-self.fontHeight)
self.numItems+=1
self.__adjustCanvasLength(self.numItems)
if self.autoFocus:
self.focusViewOnItem(index)
if self.colorChange:
Sequence(
button.colorScaleInterval(self.colorChangeDuration,self.newItemColor,Vec4(1,.2,0,1)),
button.colorScaleInterval(self.colorChangeDuration,Vec4(1,1,1,1),self.newItemColor)
).start()
def focusViewOnItem(self,idx):
"""
Scroll the window so the newly added item will be displayed
in the middle of the window, if possible.
"""
Zpos=(idx+.7)*self.itemVertSpacing-self.frameHeight*.5
self.__updateCanvasZpos(Zpos)
def setAutoFocus(self,b):
"""
set auto-view-focus state of newly added item
"""
self.autoFocus=b
def index(self,button):
"""
get the index of button
"""
if not button in self.buttonsList:
return None
return self.buttonsList.index(button)
def getSelected(self):
"""
get the currently selected button,
returns None if no button is selected
"""
return self.focusButton
def getSelectedIndex(self):
"""
get the currently selected button index,
returns None if no button is selected
"""
return self.index(self.focusButton)
def getNumItems(self):
"""
get the current number of items on the list
"""
return self.numItems
def disableItem(self,i):
if not 0<=i<self.numItems:
print 'DISABLING : invalid index (%s)' %i
return
self.buttonsList[i]['state']=DGG.DISABLED
self.buttonsList[i].setColorScale(.3,.3,.3,1)
def enableItem(self,i):
if not 0<=i<self.numItems:
print 'ENABLING : invalid index (%s)' %i
return
self.buttonsList[i]['state']=DGG.NORMAL
self.buttonsList[i].setColorScale(1,1,1,1)
def removeItem(self,index):
if not 0<=index<self.numItems:
print 'REMOVAL : invalid index (%s)' %index
return
if self.numItems==0: return
if self.focusButton==self.buttonsList[index]:
self.focusButton=None
self.buttonsList[index].removeNode()
del self.buttonsList[index]
self.numItems-=1
for i in range(index,self.numItems):
self.buttonsList[i].setZ(self.buttonsList[i],self.fontHeight)
self.__adjustCanvasLength(self.numItems)
def destroy(self):
self.clear()
self.__exitingFrame()
self.ignoreAll()
self.frame.removeNode()
taskMgr.remove('mouseInRegionCheck')
def hide(self):
self.frame.hide()
self.isMouseInRegion=False
self.__exitingFrame()
taskMgr.remove('mouseInRegionCheck')
def show(self):
self.frame.show()
if not hasattr(self,'frameRegion'):
taskMgr.doMethodLater(.2,self.__getFrameRegion,'getFrameRegion')
elif not taskMgr.hasTaskNamed('mouseInRegionCheck'):
taskMgr.add(self.__mouseInRegionCheck,'mouseInRegionCheck')
def toggleVisibility(self):
if self.frame.isHidden():
self.show()
else:
self.hide()
def sort(self,reverse=False):
buttonsTexts=[(b['text'],b) for b in self.buttonsList]
buttonsTexts.sort()
if reverse:
buttonsTexts.reverse()
self.buttonsList=[bt[1] for bt in buttonsTexts]
for i in range(self.getNumItems()):
Zpos=(-.7-i)*self.itemVertSpacing
self.buttonsList[i].setPos(.02,0,Zpos)
if __name__=='__main__':
import direct.directbase.DirectStart
import string,sys
from random import uniform
selectedButton=None
def itemSelected(item,index,button): # don't forget to receive item, it's index, and the button
global selectedButton
selectedButton=button
print item,'(idx : %s) SELECTED' %index
def itemRightClicked(item,index,button): # don't forget to receive item, it's index, and the button
print item,'(idx : %s) RIGHT-CLICKED' %index
def addNewItem():
num=btnList.getNumItems()
i='new item '+str(num)
btnList.addItem(text=i, extraArgs='NEW_%s'%num)
def insertNewItemAtSelected():
num=btnList.getNumItems()
i='new item '+str(num)
btnList.addItem(text=i, extraArgs='NEW_%s'%num, atIndex=selectedButton)
def insertNewItemBelowSelected():
index=btnList.index(selectedButton)
num=btnList.getNumItems()
if index==None:
index=num-1
i='new item '+str(num)
btnList.addItem(text=i, extraArgs='NEW_%s'%num, atIndex=index+1)
def disableRandomItem():
btnList.disableItem(int(uniform(0,btnList.getNumItems())))
def enableRandomItem():
btnList.enableItem(int(uniform(0,btnList.getNumItems())))
def removeRandomItem():
btnList.removeItem(int(uniform(0,btnList.getNumItems())))
# 3d node
teapot=loader.loadModel('teapot')
teapot.reparentTo(render)
OnscreenText(text='''[ SPACE ] : toggle visibility [ S ] : sort list [ DEL ] : destroy list
[ MOUSE WHEEL ] inside scrollable window : scroll window
[ MOUSE WHEEL ] outside scrollable window : change teapot scale
hold [ ENTER ] : add new item at the end of list
hold [ INSERT ] : insert new item at selected item
hold [ CTRL-INSERT ] : insert new item below selected item
''', scale=.045, fg=(1,1,1,1), shadow=(0,0,0,1)).setZ(.96)
OnscreenText(text='''[ CAPS LOCK] : disable random item
[ TAB ] : enable random item
hold [ X ] : remove random item
''', scale=.045, fg=(1,1,1,1), shadow=(0,0,0,1)).setZ(-.75)
# create ScrolledButtonsList
btnList = ScrolledButtonsList(
parent=None, # attach to this parent node
frameSize=(.8,1.2), buttonTextColor=(1,1,1,1),
font=None, itemScale=.045, itemTextScale=1, itemTextZ=0,
# font=transMtl, itemScale=.05, itemTextScale=1, itemTextZ=0,
command=itemSelected, # user defined method, executed when a node get selected,
# receiving extraArgs (which passed to addItem)
contextMenu=itemRightClicked, # user defined method, executed when a node right-clicked,
# receiving extraArgs (which passed to addItem)
autoFocus=0, # initial auto view-focus on newly added item
colorChange=1,
colorChangeDuration=.7,
newItemColor=(0,1,0,1),
rolloverColor=(1,.8,.2,1),
suppressMouseWheel=1, # 1 : blocks mouse wheel events from being sent to all other objects.
# You can scroll the window by putting mouse cursor
# inside the scrollable window.
# 0 : does not block mouse wheel events from being sent to all other objects.
# You can scroll the window by holding down the modifier key
# (defined below) while scrolling your wheel.
modifier='control' # shift/control/alt
)
# populate the list with alphabets and numbers
for a in string.ascii_lowercase+string.digits:
# extraArgs will be passed to command and contextMenu
btnList.addItem(text='item '+(a+' ')*10, extraArgs=a,)
btnList.setAutoFocus(1) # after adding those items,
# enable auto view-focus on newly added item
DO=DirectObject()
# events for btnList
DO.accept('space',btnList.toggleVisibility)
DO.accept('delete',btnList.destroy)
DO.accept('enter',addNewItem)
DO.accept('enter-repeat',addNewItem)
DO.accept('insert',insertNewItemAtSelected)
DO.accept('insert-repeat',insertNewItemAtSelected)
DO.accept('control-insert',insertNewItemBelowSelected)
DO.accept('control-insert-repeat',insertNewItemBelowSelected)
DO.accept('caps_lock',disableRandomItem)
DO.accept('tab',enableRandomItem)
DO.accept('x',removeRandomItem)
DO.accept('x-repeat',removeRandomItem)
DO.accept('s',btnList.sort)
# events for the world
DO.accept('wheel_up',teapot.setScale,[teapot,1.2])
DO.accept('wheel_down',teapot.setScale,[teapot,.8])
DO.accept('escape',sys.exit)
camera.setPos(11.06, -16.65, 8.73)
camera.setHpr(42.23, -25.43, -0.05)
base.setBackgroundColor(.2,.2,.2,1)
base.disableMouse()
run()
|
_________________ http://ynjh.panda3dprojects.com | http://ynjh.p3dp.com
Intel P4Prescott 2.8GHz HT | Elixir 1.5GB | ATI HD4670 1GB GDDR3
Last edited by ynjh_jo on Sun May 16, 2010 10:25 am; edited 2 times in total |
|
ColShag
 Posts: 59
 |
|
ColShag
 Posts: 59
 |
Hey ynjh, Thanks again for posting this code, I can see you put a lot of effort into this List Control and I love it! Its very full featured with the scrollable mouse wheel support, etc... I was able to easily put it into the Asteroids Sample as a test.
Couple Questions for you (or anyone interested)
1) I noticed a significant frame rate drop if I expanded the window or went full-screen, is this normal when adding panda3d gui?
2) I can live with 1) as long as there is a way to remove the overhead if I switch modes within my game (ie, is there a clean way to remove the list? I tried using the Destroy method, but the frame rate did not go back up, I tried setting List=None, that didn't help either, any suggestions?
3) ynjh, have you thought of expanding your obvious gui creating talents and making a nice gui toolkit for us developers to use? I can see you put a lot of effort into this ListView (and the treeview). I have seen that someone was starting a gui toolkit, but that kinda died off.
Thanks again,
C |
|
treeform
 Posts: 2027 Location: Seattle
 |
|
ynjh_jo
 Posts: 1596 Location: Malang, Indonesia
 |
1. I'm not sure if it's caused by the 2d scene, but I could be wrong though. What resolution for fullscreen did you use ? The same like when windowed ? If so, then there must be a deep problem.
2. I'll take a deeper look. I'm not on my dev pc.
3. Sorry, I don't have time for that.
ColShag, while you're around, do you mind telling us about your experience with PyUI ? The pros and cons.
I guess it's possible to use it within panda, as within PyGame, since it simply calls PyGame's drawing commands, similar like using LineSegs in Panda for lines, and so on. _________________ http://ynjh.panda3dprojects.com | http://ynjh.p3dp.com
Intel P4Prescott 2.8GHz HT | Elixir 1.5GB | ATI HD4670 1GB GDDR3 |
|
ColShag
 Posts: 59
 |
treeform: That's great that you are already using it, I'm sure its the last thing on your mind, but if you could share the results I would appreciate it greatly (see if it would work out for my potential port).
ynjh: Yeah I've noticed that if I expand the window simply the framerate seems to half usually. I'm unsure of why, but more importantly, if I remove the listbox the framerate does not change at all (ie go back to the higher framerate), that worries me more.
As for pyui, yes, I've used it quite a bit with my game. I basically used the pyui, plus the simple open-source engine that the pyui author made as a part of his python game programming book to successfully make a full fledged game. It was quite successful. The drawbacks are that pyui depends on pyOpenGl, which is not being well supported, when I went to ubuntu-gutsy I noticed serious framerate problems so I started to look seriously at panda3d again.
Pyui seems to be setup to allow for rendering using other engines, certainly panda3d would be a possible option. I have looked at the source to see what kind of work that would be but I have not gone down that road as I'm afraid it might be a bit beyond my abilities as I am very new to panda3d.
I'll share some screenshots of pyui within my game (these are a bit old, but you will get the idea).
Ok, I would share, but I don't have a website setup right now (my game website is down for now), so any suggestions on how to post to this forum?
Thanks,
C |
|
treeform
 Posts: 2027 Location: Seattle
 |
|
ynjh_jo
 Posts: 1596 Location: Malang, Indonesia
 |
It's not the 2d scene as far as I can see. Lower framerate is normal for greater resolution.
The results here (simply that scene, idle) are :
1. normal window 800x600
shown : 202 fps
hidden : 265 fps
2. maximized window 1014x705
shown : 158 fps
hidden : 188 fps
3. fullscreen 800x600
shown : 217 fps
hidden : 564 fps
Obviously going fullscreen naturally speeds things up, I guess the route is different. I haven't read any technical explanation about this.
If removing/hiding the list didn't restore the framerate, you're in trouble I can't see.
In clear(), try replacing c.remove with c.destroy, see if it help, but I doubt it since I couldn't see the difference.
Have you tried imageshack.net ? _________________ http://ynjh.panda3dprojects.com | http://ynjh.p3dp.com
Intel P4Prescott 2.8GHz HT | Elixir 1.5GB | ATI HD4670 1GB GDDR3 |
|
ColShag
 Posts: 59
 |
I have to say I'm really impressed with you guys, I really appreciate your help on this.
ynjh: That's great news that you seem to have a new framerate when hidden, I guess I must be doing something wrong here, certainly that is possible as my experience with pandad3d is very limited. I'm running ubuntu gutsy with python 2.5 if that makes any difference.
treeform: I really appreciate your offer to make an example for me, now how to go about it? First off let me explain the game. Its a multiplayer client/server space strategy type game where you manage an empire of systems, ships, research, diplomacy etc... My friends really enjoy it, but I've decided to try and improve the client now that I am using a somewhat dead engine/ui.
As you can guess it requires some gui, rendered of course. Mainly I've managed to use pyui in its somewhat limited form. I use a lot of selectable lists, nothing as fancy as what ynjh made, but they did the job. I used buttons, textboxes, etc... The key for me was being able to select an object with the mouse which would bring up the gui as required. I used a mode system to traverse the game, I would be interested in your input on a good way to transition between modes in panda3d. By modes I mean allowing the game to switch between different scenes and properly load an unload objects, gui, etc. From reading the documentation I'm thinking maybe of the finite state machine logic for that. Of course an example of how you fine gents would do this would be helpful. One thing I've noticed from looking at most of the code in the examples, it seems that everything is thrown into the world object and then that object is run.
Modes in these examples are methods of that world object. I would prefer personally to have modes as their own objects, inheriting from a central node object with functions that all modes use. The player would then start in a mode and depending on actions close one mode and go to another. I like this method as it separates the code which can get messy when dealing with all the client and client/server logic involved. What are your thoughts on this?
There are two main functions of the game, the first is building the empire, research, diplomacy, fleet and army movement. The second is watching ship battle results. The Ship Battles are where I'm most worried about performance issues, but I'm sure Panda could handle it as its not that crazy. The other advantage of panda that I can see is the nice 3d abilities, currently my game is fully 2d, but I would like to transition to a mix of the two in the future.
I tried to setup image shack, as per your suggestion, here are a few screen shots... (hopefully they come through)
This is the research screen, beakers are selected and the gui panel allows for research, pretty basic here.
By colshag
This is the ship design screen, allows for ship design, you add components and weapons. I use a lot of list boxes, which work ok. Notice the 3 tabs at the top of the gui on the right, does panda allow for tabs somehow?
By colshag
This is the main galaxy screen, I have one panel opening another.
By colshag
This is a slightly older screenshot of the ship battle viewer. Battles are viewed as an afterfact. The player cannot "control" the ships, however he can give them orders before they go off to fight, much like a real emperor could in my opinion.
By colshag
Thanks again,
C |
|
ColShag
 Posts: 59
 |
I'm actually using your ButtonList in my game (attempting), seems to work well, thanks for the post. Small bug fix:
| Code: | def clear(self):
"""
clear the list
"""
for c in self.buttonsList:
c.remove()
self.buttonsList=[]
self.focusButton=None
self.numItems=0 |
|
|
ynjh_jo
 Posts: 1596 Location: Malang, Indonesia
 |
|
ColShag
 Posts: 59
 |
Yes, thanks ynjh for all your help and sharing your code. I am still working on my game, its getting close, and I do use an old version of your scrolled button list which works great.
C |
|
ynjh_jo
 Posts: 1596 Location: Malang, Indonesia
 |
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
| | |