Problem with DirectScrolledList in a FileDialog class

I am new developer in Panda-3D and I vritten this FileDialog class.

## Imports --------------------------------------------------------------------
import sys, os
if sys.platform == 'darwin':
    sys.path.insert(0, "/Developer/Panda3D/lib")
#import logging

import direct.directbase.DirectStart
from pandac.PandaModules import TextNode
from direct.gui.DirectGui import DirectFrame, DirectLabel, DirectSlider
from direct.gui.DirectGui import DirectButton, DirectScrolledList, DGG
from direct.showbase.DirectObject import DirectObject

## Logging configuration ------------------------------------------------------
#log = logging.getLogger(__name__)       #: the module level logger
#log.setLevel(logging.DEBUG)

#
# -----------------------------------------------------------------------------
#

class FileDialog():
    """ File dialog handler
    
    This class open a semi-transparent dialog window on the 'aspect2d' renderer.
    The user can also navigate in the specified directory and select a file or
    a directory; files can be filtered by there extention. If a directory is
    selected, it is explored recursively; if a file is selected the dialog
    window is closed and the absolute file path is returned in the callback
    function. A "Cancel" button closes the dialog window returning 'None'.
    """
    cmtt12 = loader.loadFont('cmtt12.egg')

    def __init__(self, title, basePath, ext, command):
        """ Constructor: launch the dialog window
        
        @param title (string) : title of the dialog 
        @param basePath : path of the root directory to explore
        @param ext : extension used to filter file names (no filtering if it is
                     'None')
        @param command : callback function called at close time; this function
                         muss have an argument to get the selected file path.
        """
        self.command = command
        self.path = os.path.abspath(basePath)
        self.first = os.path.basename(self.path)
        self.ext = ext
        self.curDir = self.first
        self.frm = DirectFrame(frameSize = (0, 1.5, -1, 1),
                               frameColor = (0.4, 0.9, 0.9, 0.3),
                               relief = DGG.GROOVE,
                               borderWidth = (0.02, 0.02))
        self.frm.setPos(-1.3, 0, 0)
        self.frm.reparentTo(aspect2d)
        titleLabel = DirectLabel(text = title, 
                                 scale = 0.09,
                                 pos= (0.08, 0, 0.9), 
                                 frameSize = (-0.2, 10, -0.2, 0.8), 
                                 frameColor = (0.4, 0.9, 0.9, 0.4),
                                 text_align = TextNode.ALeft)
        titleLabel.reparentTo(self.frm)
        btnCancel = DirectButton(text = "Cancel", scale = 0.07, 
                                pos = (1.326, 0, .9),
                                frameSize = (-1.58, 1.75, -0.3, 0.9),
                                frameColor = (0.4, 0.9, 0.9, 0.3),
                                command = self._cancel)
        btnCancel.reparentTo(self.frm)
        self.dirFrame = None
        self._handleADir()
        
    def _handleADir(self):
        """ Handle a directory, displays the scrolled list of dir and files.
        """
        print self.curDir, self.path
        files = sorted(filter(lambda x: os.path.isfile(os.path.join(self.path,
                                                                    x)),
                              os.listdir(self.path)))
        if self.ext != None:
            files = filter(lambda x: x.endswith(self.ext), files)
        dirs = sorted(filter(lambda x: os.path.isdir(os.path.join(self.path,
                                                                  x)),
                             os.listdir(self.path)))
        self.dirFrame = DirectFrame(frameSize = (0.05, 1.45, -0.97, 0.85),
                               frameColor = (0.4, 0.9, 0.9, 0.2),
                               )
        self.dirFrame.reparentTo(self.frm)
        dirId = DirectLabel(text = "> " + self.curDir, 
                            scale = 0.075,
                            pos= (0.085, 0, 0.76), 
                            frameSize = (-0.2, 17.9, -0.3, 0.85), 
                            frameColor = (0.4, 0.9, 0.9, 0.3),
                            text_align = TextNode.ALeft)
        dirId.reparentTo(self.dirFrame)
        dirItems = []
        if self.curDir != self.first:
            aDir = '..'
            b = DirectButton(text = (aDir, aDir, aDir, aDir), 
                             frameColor = (0, 0, 0, 0),
                             text_scale = 0.07, borderWidth = (0.005, 0.005),
                             relief=2,
                             command = self._dirSelected, 
                             extraArgs = [aDir])
            dirItems.append(b)
        for aDir in dirs:
            b = DirectButton(text = (aDir, aDir, aDir, aDir), 
                             frameColor = (0, 0, 0, 0),
                             text_scale = 0.07, borderWidth = (0.005, 0.005),
                             relief=2,
                             command = self._dirSelected, 
                             extraArgs = [aDir])
            dirItems.append(b)
        for aFile in files:
            b = DirectButton(text = (aFile, aFile, aFile, aFile), 
                             frameColor = (0, 0, 0, 0),
                             text_scale = 0.07, 
                             command = self._fileSelected, 
                             extraArgs = [aFile])
            dirItems.append(b)
        print len(dirItems), dirItems
        scList = DirectScrolledList(
                    pos = (0.07, 0, -0.95),
                    frameSize = (0.0, 1.36, 0.0, 1.67),
                    frameColor = (0.4, 0.9, 0.9, 0.3),
                    decButton_pos= (0.68, 0, 1.60),
                    decButton_text = "Previous",
                    decButton_text_scale = 0.04,
                    decButton_borderWidth = (0.005, 0.005),
                    decButton_frameColor = (0.4, 0.9, 0.9, 0.3),
                    incButton_pos= (0.68, 0, 0.04),
                    incButton_text = "Next",
                    incButton_text_scale = 0.04,
                    incButton_borderWidth = (0.005, 0.005),
                    incButton_frameColor = (0.4, 0.9, 0.9, 0.3),
                    itemFrame_frameSize = (-0.68, 0.68, -1.45, 0),
                    itemFrame_pos = (0.68, 0, 1.5),
                    itemFrame_frameColor = (0, 0, 0, 0),
                    items = dirItems,
                    numItemsVisible = 15,
                    forceHeight = 0.09
                    )
        scList.reparentTo(self.dirFrame)

    def _dirSelected(self, arg):
        """ Callback method called at directory selection
        
        @param arg    : local name of the selected directory
        """ 
        # print "Directory selected: ", os.path.join(curDir, arg)
        self.dirFrame.removeNode()
        if arg == '..':
            self.curDir = os.path.dirname(self.curDir)
            self.path = os.path.dirname(self.path)
        else:
            self.curDir = os.path.join(self.curDir, arg)
            self.path = os.path.join(self.path, arg)
        self._handleADir()

    def _fileSelected(self, arg):
        """ Callback method called at file selection.
        
        This method close the dialog window and call the callback function that
        has been passed to the constructor with the absolute path of the
        selected file as argument.       
        @param arg    : local name of the selected directory
        """ 
        print "File selected: ", arg
        self.frm.removeNode()
        self.command(os.path.join(self.path, arg))
        
    def _cancel(self):
        """ Callback method called at file selection.
        
        This method close the dialog window and call the callback function that
        has been passed to the constructor with 'None' as argument.       
        """ 
        # print "Canceled"
        self.frm.removeNode()
        self.command(None)
#
#  Testing function -----------------------------------------------------------
#
class TestDialog(DirectObject):
    
    def __init__(self):
        """ Test class constructor: executes the test
        """
        from direct.actor.Actor import Actor   # only used by test
        
        # A Pando only to have a active display
        base.disableMouse()
        base.camera.setPos(0, -10, 0)
        base.setBackgroundColor(0,0,0)
        self.pandaActor = Actor("models/panda-model",
                                {"walk": "models/panda-walk4"})
        self.pandaActor.setScale(0.003)
        self.pandaActor.setPos(0, 0, -1)
        self.pandaActor.setTwoSided(True)
        self.pandaActor.loop("walk")
        self.pandaActor.reparentTo(render)
        # Always use 'Esc' to quit
        self.accept("escape", sys.exit, [0])        
        # Connect the file dialog to "F9" key
        self.accept('f9', self.callDialog)

    def callDialog(self):
        """ Callback method to open the file dialog
        """
        frmDialog = FileDialog("Scripts loader", 'conf/default/scripts',
                               '.iscr', self.dialogCallback)

    def dialogCallback(self, aPath):
        """ Callback function connected to FileDialog
        
        @param aPath : the full path of the choosed file or 'None' if cancelled.
        """
        if aPath == None:
            print "Cancelled"
        else:
            print "Selected file: ", aPath

if __name__ == '__main__':
    
    w = TestDialog()
    run()
# -----------------------------------------------------------------------------

It seem to work fine, but the mouse selection in the DirectScrolledList does not return the selected item, but the next in the list. If you have an idea on the origin of this problem, I am interested.

Sorry for my bad english.

Daniel

Hi,

I answer to my own question: the problem is in the DirectButton(s) definition

        for aDir in dirs: 
            b = DirectButton(text = (aDir, aDir, aDir, aDir), 
                             frameColor = (0, 0, 0, 0), 
                             text_scale = 0.07, borderWidth = (0.005, 0.005), 
                             relief=2, 
                             command = self._dirSelected, 
                             extraArgs = [aDir]) 
            dirItems.append(b) 
        for aFile in files: 
            b = DirectButton(text = (aFile, aFile, aFile, aFile), 
                             frameColor = (0, 0, 0, 0), 
                             text_scale = 0.07, 
                             command = self._fileSelected, 
                             extraArgs = [aFile]) 
            dirItems.append(b) 

The directories buttons have ‘borderWidth = (0.005, 0.005), relief=2’ options defined and not the files button. If I put this options in the files buttons, the seclection is ok. In fact, only the ‘borderWidth’ option has a role, the ‘relief’ option can be removed without effect.

It works now but I not understand why :frowning:
Any idea?

Daniel