JMP 中的多文件导入工具可以创建一个包含其找到的文件的名称/大小/日期的数据表。 附加插件(JMP 16,仅限 Windows)中的 JSL 获取该数据表并创建文件使用的磁盘空间的嵌套树视图。 速度为中速;它在大约30分钟内从C盘根目录加载了500K文件(无聊,看了新闻,不确定......)有一些文件类型的查看器; JSL如下所示。
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.不从根源开始会更快。
JSL 开始时会提示输入目录。 这总是看起来使用起来很笨拙,因为双击不会选择目录......它会打开它。 突出显示一个目录并选择“确定”。 该目录被提供给 MFI 并检索文件列表。 在此过程中会进行几次取消检查。
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>
一些修改后的代码来自带有取消按钮的进度条这将在最后回收进度条窗口。
// 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>
现在是时候加入了。 此代码遍历数据表,构建反映磁盘上文件夹树形状的树。 完成后,数据表将关闭,因为所有数据都位于关联数组中的树中。 将树连接在一起的链接实际上是关联数组中的键。
// 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
检查取消,创建漂亮的图标,设置进度条的后半部分。 是的,制作您自己的自定义图标就这么简单! 通过更改从左到右的红色到灰色和红色阴影,图标对于大范围的文件大小更具表现力。
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
);
explore函数是递归的;它探索关联数组中的树并构建排序的树节点控件。
// 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] );
);
);
TreeClickHandler 为几种文件类型打开一个简单的查看器。
// 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>
最后,创建根节点,将其传递给explore(),创建一个带有树框的窗口来显示树节点控件。 有一个按钮可以打开()文件;你应该小心地按下它。 有(未显示)可执行文件扩展名的简短列表,但肯定不全面,以尽量让您摆脱麻烦......
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 picture照片
A tableA tableA tableA tableA table一张桌子
A journalA journalA journalA journalA journal日记
如果您安装了该插件,则可以使用 view->addins 查看 JSL。 您还可以下载它并使用 .zip 扩展名重命名它。
(该 JSL 的第一遍使用了大纲节点,它比树节点允许更多的附加功能。 但轮廓节点不太适合显示 100K+ 的嵌套节点;树节点是为这项工作而设计的。 如果您发现一些剩余的大纲参考(我想我清理了它们......)那就是发生的事情。)