Das Tool zum Importieren mehrerer Dateien in JMP kann eine Datentabelle mit Namen/Größe/Datum der gefundenen Dateien erstellen. Die JSL im angehängten Add-In (JMP 16, nur Windows) übernimmt diese Datentabelle und erstellt eine verschachtelte Baumansicht des von den Dateien verwendeten Speicherplatzes. Es ist mittlere Geschwindigkeit; Es hat in etwa 30 Minuten 500.000 Dateien aus dem Stammverzeichnis des Laufwerks C geladen (ich habe mich gelangweilt, die Nachrichten gesehen, bin mir nicht genau sicher ...)Für einige Dateitypen gibt es einen Viewer. JSL unten gezeigt.
Not starting at the root is faster.Not starting at the root is faster.Not starting at the root is faster.Not starting at the root is faster.Not starting at the root is faster.Nicht an der Wurzel anzufangen ist schneller.
Die JSL beginnt mit einer Eingabeaufforderung für ein Verzeichnis. Dies scheint immer umständlich zu sein, da ein Doppelklick kein Verzeichnis auswählt, sondern es öffnet. Markieren Sie ein Verzeichnis und klicken Sie auf „OK“. Das Verzeichnis wird an MFI übergeben und die Dateiliste abgerufen. Unterwegs werden einige Überprüfungen auf „Abbrechen“ durchgeführt.
root = Pick Directory( "show directory sizes", "$Desktop/.." );
If( root == "", Throw( "canceled" ));
// use MFI to get a recursive file list with sizes and dates
mfi = Multiple File Import(
<<SET folder=""></SET>
Etwas geänderter Code von Fortschrittsbalken mit Schaltfläche „Abbrechen“. Dadurch wird am Ende das Fortschrittsbalkenfenster neu gestartet.
// Progressbar https://community.jmp.com/t5/Uncharted/Progress-Bar-with-Cancel-Button/ba-p/433560
progressBarWidth = 500;
cancel = 0; // clear the cancel flag
New Window( "Directory Tree", // this window gets reused below
windowbox = V List Box( //
t = Text Box( "", <<SETWRAP></SETWRAP>
Und es ist Zeit einzusteigen. Dieser Code durchläuft die Datentabelle und erstellt einen Baum, der die Form des Ordnerbaums auf der Festplatte widerspiegelt. Wenn Sie fertig sind, wird die Datentabelle geschlossen, da alle Daten in der Baumstruktur des assoziativen Arrays gespeichert sind. Die Verknüpfungen, die den Baum miteinander verbinden, sind eigentlich Schlüssel im assoziativen Array.
// look at every row in the data table and build a tree in an associative array. The keys in the
// array represent directories, NOT files. Files are held in a {list} at each directory level.
// file sizes are accumulated at each of the file's ancestor directory levels, so there is
// a grand total at the root.
init = {{/*immediate files and dirs*/}, 0/*aggregate file count*/, 0.0/*aggregate bytes*/}; // all dir nodes look like this; there is no date for dirs
directory = [=> ]; // associative array to hold the nodes
directory[root] = init; // the root node is handled separately
nBytes = 3;
nDate = nCount = 2;
For( irow = 1, irow <= nRowsDtFiles & !cancel, irow += 1,
levels = Words( dtFiles:FileName[irow], "/" ); // break out the directory levels for this row's file
path = root; // the root is not part of the file's path broken out in levels[] but is needed in the real path spec
size = dtFiles:FileSize[irow];
date = dtFiles:FileDate[irow];
// root handled separately...
directory[root][nCount] += 1; // item 2 in the init list is a count of files here AND below
directory[root][nBytes] += size; // item nBytes is the size of all files here AND below
For( ilevel = 1, ilevel < N Items( levels ), ilevel += 1,
parent = path;
path = path || levels[ilevel] || "\";
If( !Contains( directory, path ), // never seen this node before? create it!
directory[path] = init; // create with an empty list
// add this directory to the parent directory, at position 1 so dirs come first
// Important: the missing value in the 2-item list signals a directory, not date or size.
Insert Into( directory[parent][1], Eval List( {Eval List( {levels[iLevel], .} )} ), 1 ); // my parent dir points to me (I'm also a dir)
);
directory[path][nCount] += 1;// count files and subdirs
directory[path][nBytes] += size;
If( Mod( irow, 1000 ) == 0,
updateProgress( irow / nRowsDtFiles );
Wait( 0 );
);
);
// every row represents a file. This is inserted at the end so files come last, but that is no
// longer important since they are sorted together by descendingaggregate size
// Important: the 2nd item (date) in this list is not missing, so it is a file
If( Is Missing( date ),
Throw( "missing date?" )
);
Insert Into( directory[path][1], Eval List( {Eval List( {levels[iLevel], date, size} )} ) ); // also a 3-item list, not 2 for a dir
);
Close( dtFiles, nosave ); // done with the dt, the data is all loaded in the associative array
Suchen Sie nach Abbrechen, erstellen Sie hübsche Symbole und richten Sie die zweite Hälfte des Fortschrittsbalkens ein. Ja, so einfach kann das Erstellen eigener benutzerdefinierter Symbole sein! Durch die Änderung sowohl des Rot-zu-Grau-Farbverlaufs von links nach rechts als auch des Rottons wirken die Symbole angesichts der großen Bandbreite an Dateigrößen etwas ausdrucksvoller.
If( cancel, // test the cancel flag
Beep(); // audio flag
left << color( "dark red" ); // visual flag something is awry
right << color( "red" );
cancelButton << setbuttonname( "Close" ); // visual flag the button function is changed
cancelButton << setscript( (cancelButton << closewindow) ); // and what to do if the button is pressed
Stop();
, //
updateProgress( 1.0 ); // people like to see the bar go to 100%
);
// make icons
redcolor = {0.627450980392157, 0.0352941176470588, 0.133333333333333};
graycolor = {0.831372549019608, 0.831372549019608, 0.831372549019608};
ncons = 64;// width, height, and n. the actual displayed icon may be smaller, the shades will help
For( i = 0, i <= ncons, i += 1,
// the width of the red and the shade of the red are both diminishing because
// there is a huge range to cover and this helps express small vs large better
H List Box(
Spacer Box( size( i, ncons ), color( RGB Color( redcolor * (i / ncons) + graycolor * (1 - i / ncons) ) ) ), // left side
Spacer Box( size( ncons - i, ncons ), color( RGB Color( graycolor ) ) ) // right side
) << savepicture( "$temp/deletemeIcon" || Char( i ) || ".png" )
);
biggestsize = Log( directory[root][nBytes] + 1 );// biggest in this tree is dark red square; near 0 is all light gray
icon = Function( {size}, // get the right icon for this file size
size = Floor( ncons * Log( size + 1 ) / biggestsize );
"$temp/deletemeIcon" || Char( size ) || ".png";
);
// update progressbar for 2nd phase this way...
updateProgress2 = Function( {fractionComplete},
leftsize = Round( .5 * progressBarWidth + progressBarWidth * fractionComplete * .5 ); // second half of progress bar
rightsize = progressBarWidth - leftsize;
left << width( leftsize );
right << width( rightsize );
t << settext( Eval Insert( "finishing up display constuction ^filesDone^ / ^nRowsDtFiles^" ) );
t << updatewindow; // this works without the wait
);
Die Explore-Funktion ist rekursiv; Es durchsucht den Baum im assoziativen Array und erstellt eine sortierte Baumknotensteuerung.
// recursive function to explore the tree loaded in the associative array and build the treenode structure
explore = Function( {path, attachpoint},
{treeNode, list, childpath, nfiles, bytes, appendTnode, appendSizes, m, newbutton},
list = directory[path][1]; // all the files and subdirs of this node
// keep two parallel lists of TreeNodes and the size of the object represented
// later, use rank() to sort them so the big objects are at the top of the display
appendTnode = {};
appendSizes = {};
For Each( {namedata}, list, // subdir or file? subdir will recurse, file is a leaf.
If( Is Missing( namedata[nDate] ), // then this is a dir {name, . }
If( N Items( namedata ) != 2,
Throw( "dir? " || Char( namedata ) ) // unexpected bug
);
childpath = path || namedata[1] || "\";
nfiles = directory[childpath][nCount];
bytes = directory[childpath][nBytes];
treeNode = Tree Node(
Eval Insert(
"\[^Format(bytes, "Fixed Dec", Use thousands separator( 1 ), 30, 0 )^ bytes in directory with ^nfiles^ items ^namedata[1]^]\"
)
);
treeNode << seticon( icon( bytes ) );
treeNode << setdata( childpath );
explore( childpath, treeNode ); // recursion! build the subtree
Insert Into( appendTnode, treeNode );
Insert Into( appendSizes, bytes );
, // else a file belonging to path { name, bytes, date}
treeNode = Tree Node(
Eval Insert(
"\[^Format(namedata[nBytes], "Fixed Dec", Use thousands separator( 1 ), 30, 0 )^ bytes updated ^round((today()-namedata[nDate])/inweeks(1))^ weeks ago ^namedata[1]^]\"
)
);
treeNode << seticon( icon( namedata[nBytes] ) );
childpath = path || namedata[1];
treeNode << setdata( childpath );
Insert Into( appendTnode, treeNode );
Insert Into( appendSizes, namedata[nBytes] );
filesDone += 1;
If( Mod( filesDone, 100 ) == 0,
Try( updateProgress2( filesDone / nRowsDtFiles ) );
Wait( 0 );
If( cancel,
Beep(); // audio flag
left << color( "dark red" ); // visual flag something is awry
right << color( "red" );
cancelButton << setbuttonname( "Close" ); // visual flag the button function is changed
cancelButton << setscript( (cancelButton << closewindow) ); // and what to do if the button is pressed
Throw( "canceled" );
);
);
)
);
m = Rank( appendsizes );
For( j = N Items( m ), j >= 1, j--, // biggest first, rank from small to large
i = m[j];
attachpoint << append( appendTnode[i] );
);
);
Der TreeClickHandler öffnet einen einfachen Viewer für einige Dateitypen.
// click handler for tree nodes
treeClickHandler = Function( {thistree, thisnode}, // a few viewers for common file types
{extension, data, tablename, nodedata, nodelabel},
nodelabel = thisnode << getlabel;
nodedata = thisnode << getdata;
// show(nodelabel,nodedata);
line1 << settext( nodelabel );
line2 << setbuttonname( nodedata );
// show(line2<<GETBUTTONNAME>1MB, too big for this viewer, click link above to open it" ) );//
);//
, /*else if*/
Ends With( extension, "\" ), //
view << append( Text Box( "This is a directory. Click the link above to open it." ) );//
, // else
view << append( Text Box( "Not sure what this is, don't click the link unless you want JMP to open() it." ) );//
);
);
</GETBUTTONNAME>
Erstellen Sie abschließend den Wurzelknoten, übergeben Sie ihn an explore() und erstellen Sie ein Fenster mit einer Baumbox, um die Baumknotensteuerung anzuzeigen. Es gibt eine Schaltfläche, die Dateien öffnet(); Sie sollten vorsichtig sein, wenn Sie darauf drücken. Es gibt (nicht gezeigt) eine kurze Liste ausführbarer Dateierweiterungen, die jedoch sicherlich nicht vollständig ist, um Sie vor Problemen zu bewahren ...
rootTreeNode = Tree Node( Eval Insert( "\[^Format(directory[root][nBytes], "Fixed Dec", Use thousands separator( 1 ), 30, 0 )^ ^root^]\" ) );
rootTreeNode << seticon( icon( directory[root][nBytes] ) );
explore( root, rootTreeNode );
windowbox = windowbox << parent;
(windowbox << child) << delete;
windowbox << append(
H Splitter Box(
size( 1000, 600 ),
treebox = Tree Box( rootTreeNode ),
vlistbox = V List Box(
line1 = Text Box(), // also updated by treeClickHandler
line2 = Button Box( "", // gets link-style, below. Visible name updated by treeClickHandler script.
setfunction( // when the button clicks, use its current visible name to open() the file.
Function( {this},
name = this << getbuttonname;// the underlined button name *is* the file name
extension = Uppercase( Regex( name, ".*?([^\.]*)$", "\1" ) );
If( !Contains( executable, extension ), // seems a bad idea
Open( name, Add to Recent Files( 0 ) )
,
Beep()
);
)
)
),
scrollbox = Scroll Box( view = Border Box() )
)
)
);
windowbox << Set Window Title( "Directory Tree " || root );
line2 << UnderlineStyle( 1 );
line1 << Set Stretch( "fill", "off" );
line2 << Set Stretch( "fill", "off" );
scrollbox << Set Stretch( "off", "off" );
treebox << Set Auto Stretching( 1, 1 ) << Set Max Size( 10000, 10000 ) << User Resizable( {0, 0} );
scrollbox << Set Auto Stretching( 1, 1 ) << Set Max Size( 10000, 10000 ) << User Resizable( {0, 0} );
treebox << Set Node Select Script( treeClickHandler );
treebox << expand( rootTreeNode );
A pictureA pictureA pictureA pictureA pictureEin Bild
A tableA tableA tableA tableA tableEin Tisch
A journalA journalA journalA journalA journalEin Journal
Wenn Sie das Add-In installieren, können Sie über view->addins einen Blick auf die JSL werfen. Sie können es auch herunterladen und mit der Erweiterung .zip umbenennen.
(Im ersten Durchgang dieser JSL wurden Gliederungsknoten verwendet, die viel mehr Schnickschnack ermöglichen als die Baumknoten. Umrissknoten eignen sich jedoch nicht gut für die Anzeige von mehr als 100.000 verschachtelten Knoten. Baumknoten sind für diese Aufgabe konzipiert. Wenn Sie einige übrig gebliebene Gliederungsverweise entdecken (ich glaube, ich habe sie bereinigt ...), ist das passiert.)
Hübsch! Wann immer ich Zeit habe, habe ich an etwas Ähnlichem gearbeitet, aber mit einem schnellen Überblick haben wir einige ähnliche Ideen und ich habe jetzt einige neue, wie die Verwendung eines assoziativen Arrays (ich lasse die Datentabelle offen, verwende sie aber zum Filtern von Dateien). ) und den Import mehrerer Dateien verwenden.
Die Endziele sind etwas anders. Mein Ziel ist es, ein Tool bereitzustellen, das bestimmte Verzeichnisse auflistet und mit JMP einen einfachen Zugriff auf die darin enthaltenen Dateien ermöglicht. Aber es wird auf jeden Fall einige Ideen in Ihrem Code geben, von denen ich lernen kann (assoziative Arrays und das Testen des Imports mehrerer Dateien fallen mir ein). Bild aus meiner aktuellen Version:

Den Work-in-Progress-Code finden Sie hier file-indexer/bin im Hauptverzeichnis · jthi-jmp-tools/file-indexer (github.com) , es sollte funktionieren, bin mir aber nicht sicher.
Ich habe etwas Ähnliches bei der Arbeit gemacht (dies ist mein persönliches Projekt), bei dem ich Gliederungsrahmen verwendet habe. Schade (oder vielleicht auch gut), dass ich nicht so gut als Skripter hinter ihnen stand, wie ich es kenne, denn das Aktualisieren dieses Codes ist ein Chaos und ein Albtraum. Meiner Meinung nach wird die Verwendung auch nervig, wenn es große Mengen an Umrissfeldern gibt.
Vielen Dank für diesen Beitrag!