def test_loader_compressed(test_pz_filename):
    """Tests for loading .pz files and the supports_compressed flag."""
    class TestLoader:
        extensions = ["test"]

        @staticmethod
        def load_file(path, options, record=None):
            return ModelRoot("loaded")

    # Test with property absent
    with registered_type(TestLoader):
        model = Loader.get_global_ptr().load_sync(
            test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is None

    # Test with property False, should give same result
    TestLoader.supports_compressed = False
    with registered_type(TestLoader):
        model = Loader.get_global_ptr().load_sync(
            test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is None

    # Test with property True, should work
    TestLoader.supports_compressed = True
    with registered_type(TestLoader):
        model = Loader.get_global_ptr().load_sync(
            test_pz_filename, LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is not None
        assert model.name == "loaded"

    # Test with property invalid type, should not register
    TestLoader.supports_compressed = None
    with pytest.raises(TypeError):
        LoaderFileTypeRegistry.get_global_ptr().register_type(TestLoader)
def test_loader_ram_cache(test_filename):
    """Tests that the Python loader plug-ins write to the RAM cache."""

    # Ensure a clean slate.
    from panda3d.core import ModelPool
    ModelPool.release_all_models()

    with registered_type(DummyLoader):
        model1 = Loader.get_global_ptr().load_sync(
            test_filename,
            LoaderOptions(LoaderOptions.LF_no_disk_cache
                          | LoaderOptions.LF_allow_instance))
        assert model1 is not None
        assert model1.name == "loaded"

        assert ModelPool.has_model(test_filename)
        assert ModelPool.get_model(test_filename, True) == model1

        model2 = Loader.get_global_ptr().load_sync(
            test_filename,
            LoaderOptions(LoaderOptions.LF_cache_only
                          | LoaderOptions.LF_allow_instance))
        assert model2 is not None
        assert model1 == model2

        ModelPool.release_model(model2)
Ejemplo n.º 3
0
    def startModelLoadingAsync(self):
        """
        NOTE: this seems to invoke a few bugs (crashes, sporadic model
        reading errors, etc) so is disabled for now...
        """
        self.monitorNP = None
        self.keyboardNP = None
        self.loadingError = False

        # force the "loading" to take some time after the first run...
        options = LoaderOptions()
        options.setFlags(options.getFlags() | LoaderOptions.LFNoCache)

        def gotMonitorModel(model):
            if not model:
                self.loadingError = True
            self.monitorNP = model

        self.loader.loadModel("monitor", loaderOptions=options, callback=gotMonitorModel)

        def gotKeyboardModel(model):
            if not model:
                self.loadingError = True
            self.keyboardNP = model

        self.loader.loadModel("takeyga_kb", loaderOptions=options, callback=gotKeyboardModel)
def test_loader_success(test_filename):
    """Tests that a normal dummy loader successfully loads."""

    with registered_type(DummyLoader):
        model = Loader.get_global_ptr().load_sync(
            test_filename, LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is not None
        assert model.name == "loaded"
Ejemplo n.º 5
0
def loadModel(modelPath):
    loader = Loader.getGlobalPtr()
    loaderOptions = LoaderOptions()
    node = loader.loadSync(Filename(modelPath), loaderOptions)
    if node is not None:
        nodePath = NodePath(node)
        nodePath.setTag('model-filename', os.path.abspath(modelPath))
    else:
        raise IOError('Could not load model file: %s' % (modelPath))
    return nodePath
Ejemplo n.º 6
0
def loadModel(modelPath):
    loader = Loader.getGlobalPtr()
    # NOTE: disable disk and RAM caching to avoid filling memory when loading multiple scenes
    loaderOptions = LoaderOptions(LoaderOptions.LF_no_cache)
    node = loader.loadSync(Filename(modelPath), loaderOptions)
    if node is not None:
        nodePath = NodePath(node)
        nodePath.setTag('model-filename', os.path.abspath(modelPath))
    else:
        raise IOError('Could not load model file: %s' % (modelPath))
    return nodePath
def test_loader_extensions(test_filename):
    """Tests multi-extension loaders."""
    class MultiExtensionLoader:
        extensions = ["test1", "teSt2"]

        @staticmethod
        def load_file(path, options, record=None):
            return ModelRoot("loaded")

    fp1 = tempfile.NamedTemporaryFile(suffix='.test1', delete=False)
    fp1.write(b"test1")
    fp1.close()
    fn1 = Filename.from_os_specific(fp1.name)
    fn1.make_true_case()

    fp2 = tempfile.NamedTemporaryFile(suffix='.TEST2', delete=False)
    fp2.write(b"test2")
    fp2.close()
    fn2 = Filename.from_os_specific(fp2.name)
    fn2.make_true_case()

    try:
        with registered_type(MultiExtensionLoader):
            model1 = Loader.get_global_ptr().load_sync(
                fn1, LoaderOptions(LoaderOptions.LF_no_cache))
            assert model1 is not None
            assert model1.name == "loaded"

            model2 = Loader.get_global_ptr().load_sync(
                fn2, LoaderOptions(LoaderOptions.LF_no_cache))
            assert model2 is not None
            assert model2.name == "loaded"
    finally:
        os.unlink(fp1.name)
        os.unlink(fp2.name)

    # Ensure that both were unregistered.
    registry = LoaderFileTypeRegistry.get_global_ptr()
    assert not registry.get_type_from_extension("test1")
    assert not registry.get_type_from_extension("test2")
def test_loader_exception(test_filename):
    """Tests for a loader that raises an exception."""
    class FailingLoader:
        extensions = ["test"]

        @staticmethod
        def load_file(path, options, record=None):
            raise Exception("test error")

    with registered_type(FailingLoader):
        model = Loader.get_global_ptr().load_sync(
            test_filename, LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is None
Ejemplo n.º 9
0
    def loadModel(self, modelPath, priority=None):
        self.notify.debug('Loading model... ' + modelPath)

        request = self.loader.makeAsyncRequest(
            Filename(modelPath),
            LoaderOptions(LoaderOptions.LFSearch | LoaderOptions.LFReportErrors
                          | LoaderOptions.LFNoCache))
        if priority is not None:
            request.setPriority(priority)
        request.setDoneEvent(self.asyncRequestDoneEvent)
        request.setPythonObject(modelPath)
        self.requests[modelPath] = (request, self.loadModelCallback,
                                    [modelPath])

        self.loader.loadAsync(request)
def test_loader_nonexistent():
    """Verifies that non-existent files fail before calling load_file."""
    flag = [False]

    class AssertiveLoader:
        extensions = ["test"]

        @staticmethod
        def load_file(path, options, record=None):
            flag[0] = True
            assert False, "should never get here"

    with registered_type(AssertiveLoader):
        model = Loader.get_global_ptr().load_sync(
            "/non-existent", LoaderOptions(LoaderOptions.LF_no_cache))
        assert model is None
        assert not flag[0]
Ejemplo n.º 11
0
    def loadModel(self,
                  modelPath,
                  loaderOptions=None,
                  noCache=None,
                  allowInstance=False,
                  okMissing=None,
                  callback=None,
                  extraArgs=[],
                  priority=None):
        """
        Attempts to load a model or models from one or more relative
        pathnames.  If the input modelPath is a string (a single model
        pathname), the return value will be a NodePath to the model
        loaded if the load was successful, or None otherwise.  If the
        input modelPath is a list of pathnames, the return value will
        be a list of NodePaths and/or Nones.

        loaderOptions may optionally be passed in to control details
        about the way the model is searched and loaded.  See the
        LoaderOptions class for more.

        The default is to look in the ModelPool (RAM) cache first, and
        return a copy from that if the model can be found there.  If
        the bam cache is enabled (via the model-cache-dir config
        variable), then that will be consulted next, and if both
        caches fail, the file will be loaded from disk.  If noCache is
        True, then neither cache will be consulted or updated.

        If allowInstance is True, a shared instance may be returned
        from the ModelPool.  This is dangerous, since it is easy to
        accidentally modify the shared instance, and invalidate future
        load attempts of the same model.  Normally, you should leave
        allowInstance set to False, which will always return a unique
        copy.

        If okMissing is True, None is returned if the model is not
        found or cannot be read, and no error message is printed.
        Otherwise, an IOError is raised if the model is not found or
        cannot be read (similar to attempting to open a nonexistent
        file).  (If modelPath is a list of filenames, then IOError is
        raised if *any* of the models could not be loaded.)

        If callback is not None, then the model load will be performed
        asynchronously.  In this case, loadModel() will initiate a
        background load and return immediately.  The return value will
        be an object that may later be passed to
        loader.cancelRequest() to cancel the asynchronous request.  At
        some later point, when the requested model(s) have finished
        loading, the callback function will be invoked with the n
        loaded models passed as its parameter list.  It is possible
        that the callback will be invoked immediately, even before
        loadModel() returns.  If you use callback, you may also
        specify a priority, which specifies the relative importance
        over this model over all of the other asynchronous load
        requests (higher numbers are loaded first).

        True asynchronous model loading requires Panda to have been
        compiled with threading support enabled (you can test
        Thread.isThreadingSupported()).  In the absence of threading
        support, the asynchronous interface still exists and still
        behaves exactly as described, except that loadModel() might
        not return immediately.
        
        """

        assert Loader.notify.debug("Loading model: %s" % (modelPath))
        if loaderOptions == None:
            loaderOptions = LoaderOptions()
        else:
            loaderOptions = LoaderOptions(loaderOptions)

        if okMissing is not None:
            if okMissing:
                loaderOptions.setFlags(loaderOptions.getFlags()
                                       & ~LoaderOptions.LFReportErrors)
            else:
                loaderOptions.setFlags(loaderOptions.getFlags()
                                       | LoaderOptions.LFReportErrors)
        else:
            okMissing = ((loaderOptions.getFlags()
                          & LoaderOptions.LFReportErrors) == 0)

        if noCache is not None:
            if noCache:
                loaderOptions.setFlags(loaderOptions.getFlags()
                                       | LoaderOptions.LFNoCache)
            else:
                loaderOptions.setFlags(loaderOptions.getFlags()
                                       & ~LoaderOptions.LFNoCache)

        if allowInstance:
            loaderOptions.setFlags(loaderOptions.getFlags()
                                   | LoaderOptions.LFAllowInstance)

        if isinstance(modelPath, types.StringTypes) or \
           isinstance(modelPath, Filename):
            # We were given a single model pathname.
            modelList = [modelPath]
            gotList = False
        else:
            # Assume we were given a list of model pathnames.
            modelList = modelPath
            gotList = True

        if callback is None:
            # We got no callback, so it's a synchronous load.

            result = []
            for modelPath in modelList:
                node = self.loader.loadSync(Filename(modelPath), loaderOptions)
                if (node != None):
                    nodePath = NodePath(node)
                else:
                    nodePath = None

                result.append(nodePath)

            if not okMissing and None in result:
                message = 'Could not load model file(s): %s' % (modelList, )
                raise IOError, message

            if gotList:
                return result
            else:
                return result[0]

        else:
            # We got a callback, so we want an asynchronous (threaded)
            # load.  We'll return immediately, but when all of the
            # requested models have been loaded, we'll invoke the
            # callback (passing it the models on the parameter list).

            cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
            i = 0
            for modelPath in modelList:
                request = self.loader.makeAsyncRequest(Filename(modelPath),
                                                       loaderOptions)
                if priority is not None:
                    request.setPriority(priority)
                request.setDoneEvent(self.hook)
                request.setPythonObject((cb, i))
                i += 1
                self.loader.loadAsync(request)
                cb.requests[request] = True
            return cb
    def loadModel(self, modelPath, loaderOptions = None, noCache = None,
                  allowInstance = False, okMissing = None,
                  callback = None, extraArgs = [], priority = None):
        """
        Attempts to load a model or models from one or more relative
        pathnames.  If the input modelPath is a string (a single model
        pathname), the return value will be a NodePath to the model
        loaded if the load was successful, or None otherwise.  If the
        input modelPath is a list of pathnames, the return value will
        be a list of NodePaths and/or Nones.

        loaderOptions may optionally be passed in to control details
        about the way the model is searched and loaded.  See the
        LoaderOptions class for more.

        The default is to look in the ModelPool (RAM) cache first, and
        return a copy from that if the model can be found there.  If
        the bam cache is enabled (via the model-cache-dir config
        variable), then that will be consulted next, and if both
        caches fail, the file will be loaded from disk.  If noCache is
        True, then neither cache will be consulted or updated.

        If allowInstance is True, a shared instance may be returned
        from the ModelPool.  This is dangerous, since it is easy to
        accidentally modify the shared instance, and invalidate future
        load attempts of the same model.  Normally, you should leave
        allowInstance set to False, which will always return a unique
        copy.

        If okMissing is True, None is returned if the model is not
        found or cannot be read, and no error message is printed.
        Otherwise, an IOError is raised if the model is not found or
        cannot be read (similar to attempting to open a nonexistent
        file).  (If modelPath is a list of filenames, then IOError is
        raised if *any* of the models could not be loaded.)

        If callback is not None, then the model load will be performed
        asynchronously.  In this case, loadModel() will initiate a
        background load and return immediately.  The return value will
        be an object that may later be passed to
        loader.cancelRequest() to cancel the asynchronous request.  At
        some later point, when the requested model(s) have finished
        loading, the callback function will be invoked with the n
        loaded models passed as its parameter list.  It is possible
        that the callback will be invoked immediately, even before
        loadModel() returns.  If you use callback, you may also
        specify a priority, which specifies the relative importance
        over this model over all of the other asynchronous load
        requests (higher numbers are loaded first).

        True asynchronous model loading requires Panda to have been
        compiled with threading support enabled (you can test
        Thread.isThreadingSupported()).  In the absence of threading
        support, the asynchronous interface still exists and still
        behaves exactly as described, except that loadModel() might
        not return immediately.
        
        """
        
        assert Loader.notify.debug("Loading model: %s" % (modelPath))
        if loaderOptions == None:
            loaderOptions = LoaderOptions()
        else:
            loaderOptions = LoaderOptions(loaderOptions)

        if okMissing is not None:
            if okMissing:
                loaderOptions.setFlags(loaderOptions.getFlags() & ~LoaderOptions.LFReportErrors)
            else:
                loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFReportErrors)
        else:
            okMissing = ((loaderOptions.getFlags() & LoaderOptions.LFReportErrors) == 0)

        if noCache is not None:
            if noCache:
                loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFNoCache)
            else:
                loaderOptions.setFlags(loaderOptions.getFlags() & ~LoaderOptions.LFNoCache)

        if allowInstance:
            loaderOptions.setFlags(loaderOptions.getFlags() | LoaderOptions.LFAllowInstance)

        if isinstance(modelPath, types.StringTypes) or \
           isinstance(modelPath, Filename):
            # We were given a single model pathname.
            modelList = [modelPath]
            gotList = False
        else:
            # Assume we were given a list of model pathnames.
            modelList = modelPath
            gotList = True
        
        if callback is None:
            # We got no callback, so it's a synchronous load.

            result = []
            for modelPath in modelList:                
                node = self.loader.loadSync(Filename(modelPath), loaderOptions)
                if (node != None):
                    nodePath = NodePath(node)
                else:
                    nodePath = None

                result.append(nodePath)

            if not okMissing and None in result:
                message = 'Could not load model file(s): %s' % (modelList,)
                raise IOError, message

            if gotList:
                return result
            else:
                return result[0]

        else:
            # We got a callback, so we want an asynchronous (threaded)
            # load.  We'll return immediately, but when all of the
            # requested models have been loaded, we'll invoke the
            # callback (passing it the models on the parameter list).
            
            cb = Loader.Callback(len(modelList), gotList, callback, extraArgs)
            i=0
            for modelPath in modelList:
                request = self.loader.makeAsyncRequest(Filename(modelPath), loaderOptions)
                if priority is not None:
                    request.setPriority(priority)
                request.setDoneEvent(self.hook)
                request.setPythonObject((cb, i))
                i+=1
                self.loader.loadAsync(request)
                cb.requests[request] = True
            return cb
Ejemplo n.º 13
0
    def __init__(self,
                 command,
                 fileBrowser=False,
                 defaultPath="~",
                 defaultFilename="unnamed.txt",
                 fileExtensions=[],
                 tooltip=None,
                 iconDir=None,
                 parent=None,
                 theme=None):
        """
        A simple file and folder browser

        command: The command that will be called on closing the browser
        fileBrowser: If set to True the browser will show files, otherwise it will only show folders
        defaultPath: The initial path the browser will be set to show
        defaultFilename: The filename that will be set by default, only usefull if fileBrowser is True
        fileExtensions: A list of extensions. Only files with those extensions will be shown. Only usefull if fileBrowser is True
        tooltip: An instance of the Tooltip class to display tooltips for certain parts of the editor
        iconDir: A directory path that contains replacement images. It must contain all required images which are:
            File.png
            Folder.png
            FolderNew.png
            FolderShowHidden.png
            FolderUp.png
            Reload.png
        parent: Another DirectGUI element which has pixel2d as root parent.
            The browser frame is placed centered so a frame for example should have equal sizes in horizontal and vertical directions
            e.g. frameSize=(-250,250,-200,200)
        """
        self.theme = theme if theme is not None else LightTheme()
        self.tt = tooltip
        self.command = command
        self.showFiles = fileBrowser
        self.fileExtensions = fileExtensions
        self.showHidden = False
        self.parent = parent

        self.imageOpts = LoaderOptions()
        self.imageOpts.set_auto_texture_scale(ATS_none)

        if self.theme.icon_dir is not None:
            self.iconDir = self.theme.icon_dir
        elif iconDir is None:
            fn = Filename.fromOsSpecific(os.path.dirname(__file__))
            fn.makeTrueCase()
            self.iconDir = str(fn) + "/icons"
        else:
            self.iconDir = iconDir
        self.selectedViewType = "Symbol"

        self.currentPath = os.path.expanduser(defaultPath)
        if not os.path.exists(self.currentPath):
            self.currentPath = os.path.expanduser("~")
        self.previousPath = self.currentPath

        if self.parent is None:
            self.parent = base.pixel2d
            self.screenWidthPx = base.getSize()[0]
            self.screenHeightPx = base.getSize()[1]
            self.position = LPoint3f(base.getSize()[0] / 2, 0,
                                     -base.getSize()[1] / 2)
        else:
            self.screenWidthPx = self.parent.getWidth()
            self.screenHeightPx = self.parent.getHeight()
            self.position = LPoint3f(0)
        self.screenWidthPxHalf = self.screenWidthPx * 0.5
        self.screenHeightPxHalf = self.screenHeightPx * 0.5

        self.mainFrame = DirectFrame(
            relief=1,
            frameSize=(-self.screenWidthPxHalf, self.screenWidthPxHalf,
                       -self.screenHeightPxHalf, self.screenHeightPxHalf),
            frameColor=self.theme.main_background,
            pos=self.position,
            parent=self.parent,
            state=DGG.NORMAL,
        )

        self.pathRightMargin = 155  # NOTE: Add 28 for each button to the right + 15px margin
        self.pathEntryWidth = self.screenWidthPx - self.pathRightMargin - 28

        # The path entry on top of the window
        self.pathEntry = DirectEntry(
            text_fg=self.theme.default_text_color,
            parent=self.mainFrame,
            relief=DGG.SUNKEN,
            frameColor=self.theme.entry_background,
            pad=(0.2, 0.2),
            pos=LPoint3f(-self.screenWidthPxHalf + 15, 0,
                         self.screenHeightPxHalf - 25),
            scale=12,
            width=self.pathEntryWidth / 12,
            overflow=True,
            command=self.entryAccept,
            initialText=self.currentPath,
            focusInCommand=base.messenger.send,
            focusInExtraArgs=["unregisterKeyboardEvents"],
            focusOutCommand=base.messenger.send,
            focusOutExtraArgs=["reregisterKeyboardEvents"],
        )

        # ----------------
        # CONTROL BUTTONS
        # ----------------
        x = self.screenWidthPxHalf - self.pathRightMargin + 18

        # RELOAD
        self.btnReload = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.icon_button_background,
            frameSize=(-14, 14, -10, 18),
            pos=LPoint3f(x, 0, self.screenHeightPxHalf - 25),
            command=self.folderReload,
            image=loader.load_texture(f"{self.iconDir}/Reload.png",
                                      loaderOptions=self.imageOpts),
            image_scale=14,
            image_pos=(0, 0, 4),
        )
        self.btnReload.setTransparency(TransparencyAttrib.M_multisample)
        if self.tt is not None:
            self.btnReload.bind(DGG.ENTER, self.tt.show, ["Reload Folder"])
            self.btnReload.bind(DGG.EXIT, self.tt.hide)

        # MOVE UP ONE FOLDER
        x += 28
        self.btnFolderUp = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.icon_button_background,
            frameSize=(-14, 14, -10, 18),
            pos=LPoint3f(x, 0, self.screenHeightPxHalf - 25),
            command=self.folderUp,
            image=loader.load_texture(f"{self.iconDir}/FolderUp.png",
                                      loaderOptions=self.imageOpts),
            image_scale=14,
            image_pos=(0, 0, 4),
        )
        self.btnFolderUp.setTransparency(TransparencyAttrib.M_multisample)
        if self.tt is not None:
            self.btnFolderUp.bind(DGG.ENTER, self.tt.show,
                                  ["Move up one level"])
            self.btnFolderUp.bind(DGG.EXIT, self.tt.hide)

        # CREATE NEW FOLDER
        x += 28
        self.btnFolderNew = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.icon_button_background,
            frameSize=(-14, 14, -10, 18),
            pos=LPoint3f(x, 0, self.screenHeightPxHalf - 25),
            command=self.folderNew,
            image=loader.load_texture(f"{self.iconDir}/FolderNew.png",
                                      loaderOptions=self.imageOpts),
            image_scale=14,
            image_pos=(0, 0, 4),
        )
        self.btnFolderNew.setTransparency(TransparencyAttrib.M_multisample)
        if self.tt is not None:
            self.btnFolderNew.bind(DGG.ENTER, self.tt.show,
                                   ["Create new folder"])
            self.btnFolderNew.bind(DGG.EXIT, self.tt.hide)

        # SHOW HIDDEN FOLDERS
        x += 28
        self.btnFolderShowHidden = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.icon_button_background,
            frameSize=(-14, 14, -10, 18),
            pos=LPoint3f(x, 0, self.screenHeightPxHalf - 25),
            command=self.folderShowHidden,
            image=loader.load_texture(f"{self.iconDir}/FolderShowHidden.png",
                                      loaderOptions=self.imageOpts),
            image_scale=14,
            image_pos=(0, 0, 4),
        )
        self.btnFolderShowHidden.setTransparency(
            TransparencyAttrib.M_multisample)
        if self.tt is not None:
            self.btnFolderShowHidden.bind(
                DGG.ENTER, self.tt.show,
                ["Show/Hide hidden files and folders"])
            self.btnFolderShowHidden.bind(DGG.EXIT, self.tt.hide)

        # TOGGLE VIEW TYPE
        x += 28
        self.btnViewType = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.icon_button_background,
            frameSize=(-14, 14, -10, 18),
            pos=LPoint3f(x, 0, self.screenHeightPxHalf - 25),
            command=self.toggleViewType,
            image=loader.load_texture(f"{self.iconDir}/ViewTypeSymbol.png",
                                      loaderOptions=self.imageOpts),
            image_scale=14,
            image_pos=(0, 0, 4),
        )
        self.btnViewType.setTransparency(TransparencyAttrib.M_multisample)
        if self.tt is not None:
            self.btnViewType.bind(
                DGG.ENTER, self.tt.show,
                ["Toggle view between Symbols and Detail list"])
            self.btnViewType.bind(DGG.EXIT, self.tt.hide)

        # --------------
        # CONTENT FRAME
        # --------------
        color = self.theme.scrollbar_controlls_color
        self.container = DirectScrolledFrame(
            relief=DGG.RIDGE,
            borderWidth=(2, 2),
            frameColor=self.theme.main_background,
            frameSize=(-self.screenWidthPxHalf + 10,
                       self.screenWidthPxHalf - 10,
                       -self.screenHeightPxHalf + 50,
                       self.screenHeightPxHalf - 50),
            canvasSize=(-self.screenWidthPxHalf + 31,
                        self.screenWidthPxHalf - 10,
                        -self.screenHeightPxHalf + 50,
                        self.screenHeightPxHalf - 50),
            pos=LPoint3f(0, 0, 0),
            parent=self.mainFrame,
            scrollBarWidth=20,
            verticalScroll_scrollSize=20,
            verticalScroll_thumb_relief=DGG.FLAT,
            verticalScroll_incButton_relief=DGG.FLAT,
            verticalScroll_decButton_relief=DGG.FLAT,
            verticalScroll_thumb_frameColor=color,
            verticalScroll_incButton_frameColor=color,
            verticalScroll_decButton_frameColor=color,
            verticalScroll_frameColor=self.theme.scroll_background,
            horizontalScroll_thumb_relief=DGG.FLAT,
            horizontalScroll_incButton_relief=DGG.FLAT,
            horizontalScroll_decButton_relief=DGG.FLAT,
            horizontalScroll_thumb_frameColor=color,
            horizontalScroll_incButton_frameColor=color,
            horizontalScroll_decButton_frameColor=color,
            horizontalScroll_frameColor=self.theme.scroll_background,
            state=DGG.NORMAL,
        )
        self.container.bind(DGG.MWDOWN, self.scroll, [0.01])
        self.container.bind(DGG.MWUP, self.scroll, [-0.01])

        # ACCEPT BUTTON
        self.btnOk = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.text_button_background,
            frameSize=(-45, 45, -6, 14),
            pos=LPoint3f(self.screenWidthPxHalf - 160, 0,
                         -self.screenHeightPxHalf + 25),
            text="ok",
            text_scale=12,
            text_fg=self.theme.default_text_color,
            command=command,
            extraArgs=[1],
        )

        # CANCEL BUTTON
        self.btnCancel = DirectButton(
            parent=self.mainFrame,
            relief=1,
            frameColor=self.theme.text_button_background,
            frameSize=(-45, 45, -6, 14),
            pos=LPoint3f(self.screenWidthPxHalf - 55, 0,
                         -self.screenHeightPxHalf + 25),
            text="Cancel",
            text_scale=12,
            text_fg=self.theme.default_text_color,
            command=command,
            extraArgs=[0])

        # SELECTED FILE ENTRY FIELD
        if self.showFiles:
            self.txtFileName = DirectEntry(
                text_fg=self.theme.default_text_color,
                parent=self.mainFrame,
                relief=DGG.SUNKEN,
                frameColor=self.theme.entry_background,
                pad=(0.2, 0.2),
                pos=LPoint3f(-self.screenWidthPxHalf + 25, 0,
                             -self.screenHeightPxHalf + 25),
                scale=12,
                width=200 / 12,
                overflow=True,
                command=self.filenameAccept,
                initialText=defaultFilename,
                focusInCommand=base.messenger.send,
                focusInExtraArgs=["unregisterKeyboardEvents"],
                focusOutCommand=base.messenger.send,
                focusOutExtraArgs=["reregisterKeyboardEvents"],
            )

        # ------------------
        # CREATE NEW FOLDER
        # ------------------
        # FRAME FOR CREATING NEW FOLDER
        self.newFolderFrame = DirectFrame(
            parent=self.mainFrame,
            relief=1,
            frameSize=(-self.screenWidthPxHalf + 10,
                       self.screenWidthPxHalf - 10, -20, 20),
            pos=LPoint3f(0, 0, self.screenHeightPxHalf - 55),
            frameColor=self.theme.popup_frame_background,
        )

        # LABEL FOR NEW FOLDER NAME ENTRY
        self.txtNewFolderName = DirectLabel(
            parent=self.newFolderFrame,
            text="New Folder Name",
            text_scale=12,
            text_fg=self.theme.default_text_color,
            frameColor=(0, 0, 0, 0),
            text_align=TextNode.ALeft,
            pos=(-self.screenWidthPxHalf + 15, 0, -3),
        )

        # ENTRY FOR THE NEW FOLDER NAME
        self.folderName = DirectEntry(
            text_fg=self.theme.default_text_color,
            parent=self.newFolderFrame,
            relief=DGG.SUNKEN,
            frameColor=self.theme.entry_background,
            pad=(0.2, 0.2),
            pos=LPoint3f(
                -self.screenWidthPxHalf + 25 +
                self.txtNewFolderName.getWidth(), 0, -4),
            scale=12,
            width=((self.screenWidthPxHalf - 25) * 2 -
                   self.txtNewFolderName.getWidth() - 100) / 12,
            overflow=True,
            command=self.entryAccept,
            initialText="New Folder",
            focusInCommand=base.messenger.send,
            focusInExtraArgs=["unregisterKeyboardEvents"],
            focusOutCommand=base.messenger.send,
            focusOutExtraArgs=["reregisterKeyboardEvents"],
        )

        # ACCEPT BUTTON FOR THE CREATE NEW FOLDER
        self.btnCreate = DirectButton(
            parent=self.newFolderFrame,
            relief=1,
            frameColor=self.theme.text_button_background,
            frameSize=(-45, 45, -6, 14),
            pos=LPoint3f(self.screenWidthPxHalf - 65, 0, -4),
            text="Create",
            text_scale=12,
            text_fg=self.theme.default_text_color,
            command=self.folderCreate,
            extraArgs=[0])
        # Hide the create new folder frame by default
        self.newFolderFrame.hide()

        # ---------------
        # UPDATE CONTENT
        # ---------------
        # Initial loading of the files and folders of the current path
        self.folderReload()

        # handle window resizing
        self.prevScreenSize = base.getSize()
        if self.parent is base.pixel2d:
            self.accept("window-event", self.windowEventHandler)