Lately I come across what I think not uninteresting problem which is a good exercise for the usage of Dynamic. Assume you have files in a directory structure (a tree) like the one you find in the AddOns/Packages subdirectory. Here a snip of it

|-- Charts.m
|-- Charts3D.m
|-- Kernel
|   `-- init.m
|-- PacletInfo.m
`-- Usage.m
|-- Benchmarking.m
|-- Kernel
|   `-- init.m
`-- PacletInfo.m
|-- BlackBodyRadiation.m
|-- Kernel
|   `-- init.m
`-- PacletInfo.m

What I would like to have is a dynamically updating row of PopupMenu's which let's me select a file or directory like this

enter image description here

There should be a possibility to select directories (like the Benchmarking/Kernel) directory as well. Note that not all PopupMenu's are always visible. Their number depend on the depth of the selected file/directory. For instance consider this case

enter image description here

What I tried so far is to extract all files with FileNames and use Gather and replacement rules to convert them into a tree-structure. In this tree structure you can access then different levels and update the PopupMenu's in dependence of each other.

I'm really keen to see nice* ideas of others.

*short, interesting, intuitive, mind-boggling

  • 3
    $\begingroup$ This sort of widget is commonly called "breadcrumb bar", if you need a way to refer to it. $\endgroup$ May 2 '13 at 16:22
  • $\begingroup$ @magma why have you done this, now I have to update this old ugly code :P $\endgroup$
    – Kuba
    Apr 11 '16 at 12:29
  • $\begingroup$ @Kuba Unfortunately I cannot stand typos :-) I am reviewing old questions relating to menus, before posting mine. $\endgroup$
    – magma
    Apr 11 '16 at 12:34

My solution is only prototype but I'll update it later. It differs from Yours approach becouse my function is blind. A priori it does not know the structure of file's tree.

And there is also ActionMenu not PopupMenu. :(

This version is not very elegant but I'm going to improve this.

Lets start and set main directory and create function for each menu:

dir = {SetDirectory[$InstallationDirectory]}; 

current = 1;
MENU[j_] := Module[{k},

SetDirectory[StringJoin@Riffle[dir[[ ;; j]], "\\"]];
Dynamic@dir[[ j]],
 With[{f = FileNames[][[ i]]},
  f :> (
   current = j + 1;
   k = f;
   If[StringCount[f, "."] == 0, 
    dir = PadRight[dir[[ ;; j]], j + 1, ""]; dir[[ j + 1]] = k]
{i, Length@FileNames[]}]

now we can try:

Dynamic@Row@Table[MENU[j], {j, current}]

Basic description - we are extending path by clicking at last MENU. If You set one of previous then "current" value is changing and cutting path to the point You set. It works only for directories now by looking for "." in name. I'm going to deal with generalization later.

Ask if there are any questions.

  • 1
    $\begingroup$ +1 You should replace "\\" by $PathnameSeparator. Remember, on good operating systems, you have the real and only / as separator. Beside that fact, that it cannot handle files, I like this one. $\endgroup$
    – halirutan
    May 1 '13 at 19:02

A rough straightforward implementation is to find all of the subpaths in your path, and generate a button that allows setting a new path at that level for each such subpath. Here's an implementation that generates the list of files/folders when you press the menu to ensure it's up to date.

pathParts[path_] := FileNameJoin/@Rest@FoldList[Append,{},FileNameSplit[path]]

pathPartsAlternatives[path_]:= FileNames[FileNameJoin[Append[Most@FileNameSplit[path], "*"]]]

pathPartButton[Dynamic[var_],path_] :=
    (subpaths=(RuleDelayed[Last@FileNameSplit@#,(var=FileNameJoin[{#, If[DirectoryQ[#], ".", ""]}])]&/@pathPartsAlternatives[path])),

pathPartsRow[Dynamic[var_]] := 
  pathPartButton[Dynamic[var], #] & /@ pathParts[var] // Row // Dynamic
  • $\begingroup$ On OSX and Linux you don't have \ as separator for directories. Otherwise this version is nice too. $\endgroup$
    – halirutan
    May 1 '13 at 19:11
  • $\begingroup$ @halirutan Thanks for correction, it should be system independent now. $\endgroup$
    – jVincent
    May 2 '13 at 7:33
  • $\begingroup$ Btw, I finally accepted one of your answers and I chose Kubas not because his approach is better/faster/cleverer, but only because he has 1/10 of your reputation and can need some more. I liked both of your implementations! That may sound weird but otherwise I wouldn't have known which to accept, so I hope it is sensible choice. $\endgroup$
    – halirutan
    Jun 10 '13 at 23:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.