[scripting] multicol column script workings

Say I define column script which is supposed to support multiple columns via multicol mode setting.

Should I set the same method for all the columns? Can I assume the method will be called once for each item regardless of number of this-script-columns used in file display?

You can, but you don't have to. You can do whatever makes sense for the particular script.

Generally, yes, so that the script is able to avoid re-calculating the same thing for multiple columns. The script should still be written to work if called separately for each columns as well (as it must since the user might only have one of the columns displayed).

The script will be given a list of requested columns and if it provides data for all of them then the method won't be called again for the same file (until a refresh or file change, etc.). If the script only provides data for one or some of the columns, Opus will call the script again to ask for the missing information. (At least, this is how I remember it working. You can always write some test code that prints a log message when it is called to verify exactly what happens.)

Short version of Leos answer or how to have only one call to a set of columns:

  • make them all use .multicol = true
  • make them all call the same method
  • make sure to also set those columns, for which you have no value for (shall be empty), assigning an empty string ("") is sufficient

To serve all columns at once and to prevent any further function call, you can use this function:

/////////////////////////////////////////////////////////////////////////////// function SetAllColumns(data, value=""){ var colEnum = new Enumerator(data.columns); for (;!colEnum.atEnd(); colEnum.moveNext()) data.columns(colEnum.item()).value = value; }With that function at the top of a multicol-method, you don't need to take care for empty columns anymore, they all get their default "emptyness" and no unnecessary function calls will be made. But don't use that for scripts that feature "non-multicol" columns or mixed-method columns.
Because DO puts all columns, regardless of their multicol property or method used, into the column map of a multicol column. Which I still think, is a very unhandy thing. o)

Now, was that post really shorter than yours Leo? I doubt it. o)

Corrected version, I did a minor change to the function before posting that triggers a syntax error.
It always turns out, that it's a bad idea to edit code and don't test it, even for the smallest changes. o)

/////////////////////////////////////////////////////////////////////////////// function SetAllColumns(data, value){ value = value || ""; var colEnum = new Enumerator(data.columns); for (;!colEnum.atEnd(); colEnum.moveNext()) data.columns(colEnum.item()).value = value; }

Why do that?

Because DO puts all columns, regardless of their multicol property or method used, into the column map of a multicol column. Which I still think, is a very unhandy thing. o)[/quote]

I've noticed that - I think it is just a bug.

To prevent any unnecessary function call. Imagine 3 columns called A, B and C - they all make use of the same method called "Triplet()".
If all three colums are added to a file display, Triplet() will be called three times if you do not provide values on the first call for the other two columns. And if you provide a value for column A and B only, Triplet() will be called once more to fetch a value for column C. That's why one should set column C to be blank ("") right on the first call to Triplet(), if you know there will no other value for it.

It's at least something that has not been thought of maybe. Now that you also think this is unexpected..
I already wrote about it right here: ColumnMap contains independent columns (ColumnMap contains independent columns..)

Having single-col columns and other sets of multicol-columns mixed into the column map, just makes things more difficult to handle. Here it prevents a safe way to set a default for all the multi-col columns. In my thread, it is about a generic cache implementation, which is not so generic anymore, because I need to redefine all columns of a multicol set, to restrict the cache to the correct columns.

It is not a bug. It is entirely intentional.

The whole point of the multicol mode is to let scripts fill in multiple columns at once. Opus gives multicol scripts a list of all columns it wants from them and the script can fill in what it can each time it is called. If anything is missing, Opus will ask the script again, using the method specific to one of the still-missing columns, until all methods have been tried or all columns are populated.

Multicol scripts should check which column(s) they are being asked for and skip unknown/unwanted requests anyway, so there is no extra code needed if your script is asked for something it can't provide via one method and then gets called via another method for the remaining details.

None of this should really matter if you follow the patterns in Jon's sample multicol scripts, which show the model way to do things.

You can think of single-col scripts as a legacy or "basic" thing. Had we seen the need for multicol scripts at the start, we probably wouldn't have ever have added the single-col mode as well, as it adds nothing. There is no reason to mix the modes and confuse things. Just do things one way or the other. Although we won't stop you mixing the modes if you really want to, it is your choice, but that isn't how it is intended and only adds complexity to your scripts because you have to think about two different ways of doing the same thing.

I don't agree to "should". They unnecesarily need to do that right now, because the column map contains all possible columns, even though many of them can have different use cases, have different methods set or belong to some other aspect of a script.

I think, checking if the column asked for is of interest to a column method is extra code and even more of an issue, it prevents easy handling and some clever techniques from working properly. The tiny method shown above, to set a default for all involved columns, is a nice example. It actually requires extra code and a seperate list of all the column names, to only touch the right ones - requiring extra dependancy management.

So, if the column map would only list those columns, that share the same method, things would be much more elegant and easy. No need to test if the column asked for can be handled and no need to create extra subsets of column names for loops and generic code to work upon the map.
Not putting all the available columns into the map, has just advantages.

Maybe I overlooked something, but given I did not, I see absolutely no reason to favor the current implementation.

Leo, I don't buy it. DOpus knows what columns need refresh, so it should request refresh only for the needed items.
Imagine a windows manager requesting GUI refresh just for all the application, and let application to figure it out what to refresh...

Now, if there are columns to refresh, and I can determine the set of columns having multicol set and the same callback function, I would call the function once for each item with set of columns to refresh, not with all columns registered for the callback.

BTW, I don't know what are 'Jon's sample multicol scripts', I cannot see anything about multicol in scripting subforum.

It does only request the needed items, doesn't it?

If a column is turned off, Opus won't request it.

Multicol scripts still have to look at which columns they are being asked for in order to know... which columns they are being asked for.


So if I don't (want to) use any kind of persistence:

  • how should I determine what are visible columns supported by my multicol function? Just walk the active tab checking column names?
  • how can I determine whether my multicol function has already been run (in columns there just all columns registered for the function in OnInit)? Should I just query one of the above columns, and if it is not empty (meaning the function has been run, presumably filing all the visible columns it supports), just return?

You don't need to determine yourself what columns are visible, the method you set for your column will be called automatically if necessary. And that is when a column is in use in a filedisplay or queried because of a label or find. You also don't need to know if your column method has been called already or not. It will be called for each column there is or just once, if your columns are of type multicol and you pass values for all the columns in the first execution of the method.

I suggest you take a look at an easy example. This one: Column: ModifiedWithin (alternative to 'modified'-labels) These scripts do not make use of multi-columns, but to understand the basics, these should be fine.

An easy multicol example script is this - just concentrate on the functions OnAddColumns() and ModifiedWithin(), the others are just helper functions: Column: ModifiedWithin (alternative to 'modified'-labels)

Regarding the last example (I don't know JScript so I may be overlooking something):

Say I have just one column visible: ModifiedWithin-Graph
Then Column_ModifiedWithin is called one time - but you still seem to fill ModifiedWithin-Chars and other columns - this is wrong, these columns are not visible.

"It will be called for each column there is or just once, if your columns are of type multicol and you pass values for all the columns in the first execution of the method" - Am I not setting multicol just exactly to avoid this? I want each multicol function to be called exactly once for each item, if 1 or more columns handled by it are visible.

I mean, the current state of things is like this:

  • I have a multicol function for, let's say, 3 columns
  • Two of these columns are displayed, two relevant items in the lister
  • The function is called 6 times (should be 2 imo)
    Now the problems:
  • How do you determine in exemplary Column_ModifiedWithin which colum you are called for?
  • How do you determine in exemplary Column_ModifiedWithin what columns are visible. So that you don't run 5 second db queries for columns that are not displayed.
  • How do you determine whether you have already filled all the columns for an item, because multicol function has already run for this item? Each whole script run is extremely costly, like couple of seconds to start an external db engine.

From the documentation: [multicol] "If this is set to True then your column handler function has the option of returning data for multiple columns simultaneously, rather than just the specific column it is being invoked for." - So why it is invoked for all the visible columns, when it is designed to avoid that? And if it is supposed to behave like it behaves now, how I avoid problems I gave 2 posts earlier?

I mean, I have stuff working, but it takes much longer that it should.

Self correct - 'it should read: "The function is called 4 times"'

Oh, and I forgot the bottom line (even if repeating myself):
How do I limit a multicol function operations to exactly one 'job' for exactly the columns that are displayed?
I don't want to execute the job more that once for each item, and I don't want to execute it for any multicol-funciton-supported columns that are not displayed. The multicol can be called any number of times. At first execution for an item I just want to know what columns I support should be filled (I want to fill any visible only), at subsequent calls for this I just want to know that I have already run and don't need to do anything.

If a visible column is flagged as supporting multi-column, Opus will ask your script to provide data for all of its columns in one go. That's the whole point of the flag.

Fine, so this is clear and what I actually observed (it seems in contrary to some things written earlier, but I must have misunderstood), and how it should work imo - I treat ScriptColumndata.col as a random column from the set I should provide values for and I am OK with that.

Now, in short - is it possible to tell in a simple way which my-script-supported columns are visible, so I am not filling the ones that are not visible? I can think of a complicated way, but is there something simple? Or is this just undesired, and multicol function should fill all its columns when called (because fe. DO counts on refreshing all this data at each call)?

Another similar scenario - I have already 3 of script columns filled, now I add 2 new and I am called for one of them - I would like to learn I need to actually provide values just for these 2, not for all the five.

BTW, multicol support works, my questions are aimed at optimisation. If you support 10 columns and behind each there is a query which takes 2 seconds to complete, there are major gains from calling just the ones that are needed. (Yes I know the calling for multiple items is my another major problem but it's not the point here - I have a solution for that) .

I get the impression, you do not want to use the multicol feature. If you cannot or don't want to query your database in a way, that you get results for all the columns at once, then just don't use multicol columns.

The multicol feature has nothing to do with how many columns your script provides. The multicol-flag on a column, as mentioned several times now, just allows you to give values to other columns, which then prevents DO from calling the methods of these other columns. In general this should reduce execution times and number of queries to a database. Your case may be different.

This is not necessarily wrong. I use a single method in that script to provide data for all the columns at once. And I do not check what the name of the column is, because it is not important, once you fill all columns. I also don't mind filling all the columns, because I have data for all of them prepared. Not filling a specific column, even though I could easily calculate a value for it, would make things less efficient.

You should only use multicol if you can return all the columns in approximately the same time as you can return one. If each column takes 2 seconds to return then you shouldn't use multicol, there would be no advantage to it.

Thanks to all, now I understand multicol things better.