Show progress window when running a command with multiple files

Im running the following code. How can I show a progress window for that command? I could simply run the command file by file and show a progress window but i guess thats really inefficient. I thought that when running a copy command a progress is shown automatically informing about the progress.

	cmd.AddFiles(...);
	cmd.AddLine("SetAttr META ...");
	cmd.Run();

Efficiency should be fine doing it that way.

You’re running SetAttr not Copy.

Sorry, my text was misleading. I realize that it is a different command. My question was whether there is a dialog for SetAttr, similar to the one for copy, which shows the progress instead of simply waiting until the command is finished.

I would also like to see some sort of progress indicator when running a command.

For example, I recently used a JScript command on tens of thousands of files to append the folder name as a Keyword to all photos in each folder.

There is no way I know of to see progress!

Is it possible for a progress indicator be created that works with commands?

Thank you, but I do not have the knowledge to follow the instructions on that page:

Here is an example of real use from a function (rather long):

function ExtractArchives(scriptCmdData, selected_files, targetPath, extractEnclosed, deepInspect, deleteEnclosed, deleteList)
{
	var warning = false;
	var skipList = new Array();
	var fsutil = DOpus.FSUtil;
	var processedArchives = DOpus.Create.Vector();
	var inspectArray = DOpus.Create.Map();
	var itemToArchive = DOpus.Create.Map();
	var cmd = scriptCmdData.func.command;
	var DEBUG = false;
	var cmdProg = DOpus.Create.Command();
	var progress = cmdProg.progress;
	var progressIndex = 0;
	var progressTotal = 0;

	progress.Init(scriptCmdData.func.sourcetab, "Smart Extract");
	progress.pause = false;
	progress.SetTitle("Smart Extract");
	progress.Show();


	logger.info("EXTRACT >>> START <<< ");
	if (selected_files.length) logger.debug("length=" + selected_files.length);
	if (selected_files.count) logger.debug("count=" + selected_files.count);
	logger.debug("EXTRACT > " + (selected_files.length || selected_files.count) + " file(s) to process.");
	logger.debug("EXTRACT > Target folder : '" + targetPath + "'.");
	logger.debug("EXTRACT > Extract enclosed archives : '" + extractEnclosed + "'.");
	logger.debug("EXTRACT > Delete enclosed archives after extract : '" + deleteEnclosed + "'.");
	logger.debug("EXTRACT > Deep inspection requested : '" + deepInspect + "'.");

	logger.info("EXTRACT >> Parsing files to extract.");
	//+dout("**** Adding files to progress : " + selected_files.count);
	if (selected_files.length) progressTotal = selected_files.length;
	else if (selected_files.count) progressTotal = selected_files.count;
	progress.AddFiles(progressTotal);
	//DOpus.Delay(250);
	for (var e = new Enumerator(selected_files); !e.atEnd(); e.moveNext()) {
		var item = e.item();
		// dout("");
		logger.info(">>" + item.name + "<<<");
		progressIndex++;
		progress.SetName("[" + progressIndex + "/" + progressTotal + "] ▶ " + item.name);

		// -------- Check Proper Archive (not archive file, folder, rxx, partN not first part)
		if (!IsProperArchive(item, selected_files)) {
			warning = true;
			skipList.push(item.name);
			progress.StepFiles(1);
			logger.warn("Skipping file in progress");
			continue;			
		}

		// -------- Check Protected, and set clipboard to default password for user interaction
		CheckProtectedArchive(item);

		// -------- Parse Archive content to identify if archive root is only 1 folder
		var archiveInFolder = GetContainedFolder(item);
		logger.debug(" Archive is fully contained in a folder (Only 1 folder in root) = '" + archiveInFolder + "'");

		// -------- Extracting Archive (Recursively calling ExtractArchive if extractEnclosed or deepInspect is true)
		var itemPath = DOpus.FSUtil.NewPath(""+item.path);
		logger.trace("Item Path = " + itemPath);
		var thisItemTargetPath = targetPath;
		if (itemPath.test_parent) itemPath.Parent;

		logger.trace("Before comp : itemPath = " + itemPath);
		logger.trace("Before comp : fsutil = " + fsutil.Resolve("/downloads/05_AudioToSort"));
		if (""+itemPath == ""+fsutil.Resolve("/downloads/05_AudioToSort")) {
			thisItemTargetPath += "\\" + item.path.filepart;
			logger.info("Dealing with a 05_AudioToSort file, replicating folder structure. (thisItemTargetPath=" + thisItemTargetPath + ").");
		}
		else {
			logger.info("Not in 05_AudioToSort ... processing as usual.");
			logger.trace("test parent = " + itemPath.test_parent);
			logger.trace("Parent = " + itemPath);
			logger.trace("fsutil resolve = " + fsutil.Resolve("/downloads/05_AudioToSort"));
		}


		var extractFolder = thisItemTargetPath;
		if (archiveInFolder != "") extractFolder += "\\" + archiveInFolder;
		else extractFolder += "\\" + item.name_stem_m;

		var containedArchives = RawExtract(scriptCmdData, item, archiveInFolder, extractFolder, thisItemTargetPath, processedArchives, extractEnclosed, deepInspect, deleteEnclosed, deleteList);

		if (deepInspect) {
			logger.info("Deep inspection into " + extractFolder);

			var dirIter = DOpus.FSUtil.ReadDir(extractFolder, true);
			while (!dirIter.complete) {
				var itemInspect = dirIter.Next();
				if (!itemInspect.InGroup("Archives") || itemInspect.is_dir) {
					if (DEBUG)
						logger.trace("Inspect | not an archive > " + itemInspect);
				}
				else {
					logger.debug("Inspect | ARCHIVE FOUND  > " + itemInspect);
					if (arrayContainsText(containedArchives, itemInspect) || "" + itemInspect === "" + item)
						logger.trace ("Inspect | ... already in extracted archives");
					else if (arrayContainsText(processedArchives, itemInspect)) {
						logger.trace("Inspect | ... already in processed archives");
					}
					else {
						logger.debug ("Inspect | NEEDS TO BE PROCESSED");
						logger.trace ("Name = " + itemInspect.name + " | Folder = " + itemInspect.path);
						// Prepare user interaction
						//if (!arrayContainsText(inspectArray, itemInspect.path))
						if (! inspectArray.exists("" + itemInspect.path))
							inspectArray("" + itemInspect.path) = DOpus.Create.Vector();
						inspectArray("" + itemInspect.path).push_back(itemInspect);
						itemToArchive("" + itemInspect) = item;
						
						// If processed (whatever decision : extract or not), consider as processed
						//processedArchives.push(itemInspect);
					}
				}
			}
		}

		// -------- Deleting sub archives
		if (!deleteEnclosed) continue;
		logger.info("* Preparing deletion for enclosed archives for " + item.name + ". " + containedArchives.length + " archives found.");
		GetFilesToDelete(item, containedArchives, deleteList);

		logger.trace("<<Delete List -- Checking (after processing item '" + item + "' >>");
		for(var i = 0; i < deleteList.length; i++)
		{
			var item = deleteList[i];
			var sourceArch = item.archive;
			var candidate = item.file;
			logger.trace(" > Arch (#" + i + "): '" + sourceArch.display_name + "' | Candidate: '" + candidate.realpath + "'");
		}
		logger.trace("Stepping file in progress");
		progress.StepFiles(1);
	}

	if (extractEnclosed) {
		var confirmedList = ConfirmExtractList(inspectArray,scriptCmdData.func.sourcetab);
		for (var f = new Enumerator(confirmedList); !f.atEnd(); f.moveNext()) {
			var deepContainedArchives = DOpus.Create.Vector();
			var insArray = f.item();
			logger.debug("Confirmed Path = " + insArray);
			for (var g = new Enumerator(confirmedList(insArray)); !g.atEnd(); g.moveNext()) {
				var insItem = g.item();
				logger.trace(" > Confirmed item = " + insItem);
				deepContainedArchives.push_back(insItem);
				if (itemToArchive.exists(insItem)) {
					GetFilesToDelete(itemToArchive(insItem), deepContainedArchives, deleteList);
				} else {
					logger.error("** Error : unable to retrieve associated Archive for deep inspected item " + insItem);
				}
			}
			var subProcessedArchives = ExtractArchives(scriptCmdData, confirmedList(insArray), insArray, extractEnclosed, deepInspect, deleteEnclosed, deleteList);
			logger.info("*** extract Deep Enclosed processed new archives");
			for (var g = new Enumerator(subProcessedArchives); !g.atEnd(); g.moveNext()) {
				var subItem = g.item();
				logger.trace("*** 1 item : " + subItem);
				processedArchives.push_back(subItem);
			}
		}

		logger.debug("<<'Final' Delete List -- Checking (after deep inspection resolution) >>");
		for(var i = 0; i < deleteList.length; i++)
		{
			var item = deleteList[i];
			var sourceArch = item.archive;
			var candidate = item.file;
			logger.trace(" > Arch (#" + i + "): '" + sourceArch.display_name + "' | Candidate: '" + candidate.realpath + "'");
		}		
	}




	// -------- Display warnings about skipped files
	if (warning) {
		logger.warn("** Warning : Some files have been skipped **");
		logger.warn("** >> Skipped files : " + skipList.join(","));
		logger.warn("**");
	}

	logger.info("EXTRACT > Exiting for targetPath = " + targetPath);
	// dout("---");
	progress.Hide();
	return processedArchives;
}

The interesting parts are:

  • Creation, Init and Show:
	var cmdProg = DOpus.Create.Command();
	var progress = cmdProg.progress;
	var progressIndex = 0;
	var progressTotal = 0;

	progress.Init(scriptCmdData.func.sourcetab, "Smart Extract");
	progress.pause = false;
	progress.SetTitle("Smart Extract");
	progress.Show();
  • Then Adding the total number of files to process (once calculated): progress.AddFiles(progressTotal);
  • Changing the display when you're processing a specific file: progress.SetName("[" + progressIndex + "/" + progressTotal + "] ▶ " + item.name);
  • Incrementing the counter of processed files: progress.StepFiles(1);
  • Once you're done, hiding the window: progress.Hide();

Thank you very much.

I am trying to make it work with a command, but that command is so different from the one you used in your example that I don’t know where to insert the lines of code.

I’ll keep trying!

This is the command I’m trying to edit to include a progress indicator.

function OnClick(clickData)
{
	var cmd = clickData.func.command;
	cmd.Clear();
	cmd.ClearFiles();

	var col = clickData.func.sourcetab.selected_files;

	if (col.count > 0)
	{
		for (var eSel = new Enumerator(col); !eSel.atEnd(); eSel.moveNext())
		{
			var item = eSel.item();
			var name = item.path.stem;

			if (name && name != "")
			{
				var line = 'SetAttr FILE="' + item.RealPath + '" META "tags:+' + name + '"';
			//	DOpus.Output(line);
				cmd.RunCommand(line);
			}
		}
	}
}

As im working on a script command where im needing that progress, ive got some code for you. You could call it a little bit overengineered but it seperates the "What needs to be done" logic from the progress logic.

function OnClick(clickData)
{
    var sourcetab = clickData.func.sourcetab;
    sourcetab.Update();

    var files = sourcetab.selected_files;
	var totalCount = sourcetab.selstats.selfiles;
    var fileEnum = new Enumerator(files);

	var cmd = clickData.func.command;
	cmd.SetSourceTab(sourcetab);

    //This is the interesting part, this is called per file
	var itemHandler = function(item, count, progress)
	{
		cmd.Clear();
		cmd.ClearFiles();

		cmd.AddFile(item);
		cmd.AddLine('SetAttr FILE="' + item.RealPath + '" META "tags:+' + item.path.stem + '"');
		cmd.Run();
		DOpus.Delay(500);//This is just to demonstrate longer progresses
	};

	var progress = clickData.func.command.Progress;
	progress.Init(sourcetab, "Setting attributes");

	ProgressForeach(progress, fileEnum, totalCount, itemHandler, null);
}

//Handle a progress that calls an itemHandler for each item in queriedItems
function ProgressForeach(progress, enumerator, totalCount, itemHandler, abortHandler)
{
	abortHandler = abortHandler || function () { return true; };

	progress.abort = true;
	progress.pause = true;
	progress.SetStatus("Setting attributes for " + totalCount + " items");
	progress.SetFiles(totalCount);
	progress.Show();

	var cnt = 1;
	while(!enumerator.atEnd())
	{
		var abortState = progress.GetAbortState();
        if (abortState == "a")
        {
			if(abortHandler(progress))
			{
				progress.Hide();
				return;
			}
		}
        else if (abortState == "p")
        {
              DOpus.Delay(500);
              continue;
        }

		var item = enumerator.item();
		itemHandler(item, cnt, progress);
		progress.SetName(item.name + " (" + cnt + "/" + totalCount + ")");
		progress.SetFilesProgress(cnt++);
		enumerator.moveNext();
	}
	progress.Hide();
}
1 Like

FWIW the cmd.AddFile(item); line isn't needed, since the file path is being given to the command via arguments.

(At the same time, it shouldn't cause any harm.)

Thanks. Didnt pay too much attention to the command itself.

Offtopic: @Leo the forum told me to link my account (on top of the my last post after you edited my it). It brings me to my account page where a button shows "account linking" and in green "account linked" next to it. I think that already happened a few times before.

It's frequent since a few months now, especially when posting/editing posts. Just refresh the page, and it should disapear.

I don't have time to test right now, but this should work:

function OnClick(clickData)
{
	var cmd = clickData.func.command;
	cmd.Clear();
	cmd.ClearFiles();
	var progress = cmd.progress;
	progress.Init(clickData.func.sourcetab, "Name your operation here");
	progress.pause = false;
	progress.SetTitle("Title of the progress bar dialog");


	var col = clickData.func.sourcetab.selected_files;

	if (col.count > 0)
	{
		progress.Show();
		progress.AddFiles(col.count);

		for (var eSel = new Enumerator(col); !eSel.atEnd(); eSel.moveNext())
		{
			progress.StepFiles(1);
			var item = eSel.item();
			var name = item.path.stem;
			progress.SetName(item.name);

			if (name && name != "")
			{
				var line = 'SetAttr FILE="' + item.RealPath + '" META "tags:+' + name + '"';
			//	DOpus.Output(line);
				cmd.RunCommand(line);
			}
		}
		progress.Hide();
	}
}
1 Like

Thank you very much. I have not yet had the change to try this.