Creating the textwave for a ListBox

Hello,

I'd like to display a listBox in the GUI I'm developping, in order to help the user to select the measurements to be plotted.
A measurement consists of several waves. Every time the user open a new measurement, a new datafolder is created.
I think that the fact that a measurement consists of many waves doesn't allow me to use the wave selection widget.
That's why I'm trying to do it by myself. I'v just begun to write the code, and I have have to afford a syntax problem ! I can't compile this portion, which is intended to update the List Wave :

(this function will be located in a loop, in order to browse all the datafolders. The datafolders contain a string called "FileName")

Function AddTo_ListWave(LIST, FileName)

string LIST
string FileName

$LIST[numpnts(LIST)-1] = $FileName

End


Would you have some advice about that please ?

Thank you !

Regards
Hello Nasser,

I don't think I understand what you want to achieve. However, there some issues with the example code you're posting:
Nasser wrote:

Function AddTo_ListWave(LIST, FileName)
string LIST
string FileName

$LIST[numpnts(LIST)-1] = $FileName

End



Your use of $fileName is incorrect and should be replaced by just fileName. This is explained in
DisplayHelpTopic "Converting a String into a Reference Using $"

The '$' operator can be confusing. However, just remember that you use $ only when Igor expects a name for some object (a wave, a window, a control, etc.), and that name is contained in the string. If Igor expects a string then you never use '$'.

Your function specification has the type of list as a string. However, your code then interprets list as the name of a text wave. I would either make list into a wave, so that you pass the wave to this function directly, or I would modify your code as follows:
wave /T listWave = $LIST
listWave[numpnts(listWave) - 1] = fileName


Lastly, the name of your function leads me to believe that you want to append an element to the list instead of replacing the last element. For that you have to remember to enlarge the wave, e.g.
wave /T listWave = $LIST
Redimension /N=(DimSize(listWave, 0) + 1) listWave
listWave[DimSize(listWave, 0) - 1] = fileName


Where I've also replaced numpnts with DimSize as it is a more capable replacement.
Nasser wrote:
I think that the fact that a measurement consists of many waves doesn't allow me to use the wave selection widget.

The WaveSelectorWidget procedure file supports multiple selections. In the function MakeListIntoWaveSelector, set selectionMode to either WMWS_SelectionNonContiguous or WMWS_SelectionContiguous as appropriate.
Quote:
That's why I'm trying to do it by myself.

The WaveSelectorWidget procedure file has some might complicated code. Such a project is probably not a great one for learning basic Igor programming!

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Hello and thank you for your help !

741 : Thank you, now it compiles, but I still have to solve some little issues.

John :
Thank you,
As you can see on the attached screenshot, a measurement consists of : 2 matrixes, 4 line profiles, plus other waveforms which are not yet displayed. These data, belonging to the same dataset, are gathered in the same data folder. What I'd like to achieve, is to select the datasets ((the 2 matrixes + the 4 line profiles)*number of selected datasets) that I want to view.
I think that the widget enables me to select only the waves, not the whole dataset. Would you have an idea ?


Best Regards
GUI.JPG
Nasser wrote:

These data, belonging to the same dataset, are gathered in the same data folder. What I'd like to achieve, is to select the datasets ((the 2 matrixes + the 4 line profiles)*number of selected datasets) that I want to view.


Instead of having the user select a bunch of waves, how about having the user select the datafolder instead? I assume that, given a datafolder, you can extract the waves you need automatically and append them to the relevant graphs.

I think you could do that fairly straightforwardly using the WaveSelectorWidget. Try passing "WMWS_DataFolders" as the content parameter to MakeListIntoWaveSelector.
actually, that's exactly what I meant. I don't want to select a bunch of waves but only the folder. Instead of selecting directly the folder names, I'd like to display a string (filename) which is in the datafolder.

Regards
Exactly. Using WaveSelectorWidget, and passing the constant WMWS_DataFolders to MakeListIntoWaveSelector:
MakeListIntoWaveSelector(panelName, listcontrolname, content = WMWS_DataFolders)

you will get a selector widget that shows just the data folders, and allows you to select them.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Nasser wrote:
Instead of selecting directly the folder names, I'd like to display a string (filename) which is in the datafolder.

Do you mean you want to select a string variable? Or do you mean that you will arrange to have a string variable in the folder, and when you select a folder it should display the contents of that string?

WaveSelectorWidget can be told to show string variables, but not to display the contents.

John Weeks
WaveMetrics, Inc.
support@wavemetrics.com
Hello,

I mean that I'd like to select the content of a string variable ('filename' or 'Nom_fichier' in the screenshot ) from the list box.
This string is supposed to identify the dataset (consisting of many matrixes and traces) that I want to plot or to remove from the graph windows.

EDIT : Code updated
//----------------------------------------------------------------------------------
Function UpdateDisplay_ListBox(Host, L,T,R,B, NbElements, LISTwave, SELwave)

string Host, LISTwave, SELwave
variable L,T,R,B, NbElements

string dfSav = GetDataFolder(1)
SetDataFolder ::

Make/O/T/N=(NbElements) $LISTwave
Make/O/B/N=(NbElements) $SELwave
ListBox lb1, win=$Host, pos={L,T}, size={R-L,B-T}, listWave=$LISTwave, selWave=$SELwave, mode= 4, proc=Proc_ListBox_SelectDataset

SetDataFolder dfSav

End
//----------------------------------------------------------------------------------
Function AddTo_ListWave(listWave, Nom_fichier, selWave)

string listWave
string Nom_fichier
string selWave

WAVE /T LISTE = root:$listWave
WAVE /B SEL = root:$selWave

NVAR Runs = root:Runs

    Redimension /N=(DimSize(SEL, 0) + 1) SEL
    SEL[DimSize(SEL, 0) - 1] = 0x20 // SpĂ©cifier la checkbox

    Redimension /N=(DimSize(LISTE, 0) + 1) LISTE   
    LISTE[DimSize(LISTE, 0) - 1] = Nom_fichier

End



These functions are called every time we open import a new dataset, while the current datafolder is 0, 1, 2... (where the datasets are stored):
UpdateDisplay_ListBox("MyPanel", 3,648,303,848, Runs, "LISTE", "SEL")
AddTo_ListWave("LISTE", Nom_fichier)

Runs += 1


Now we have to retrieve the datafolder(s) corresponding to the selected dataset(s). Provided that the order is not changed in the listbox, the row number may be sufficient.

Is there any detail that I missed ? Am I on the good way, or should I focus on the WaveSelectorWidget ? (which seems really hard to fully understand).

Thank you
DataBrowser.JPG
The WaveSelectorWidget may be somewhat less appropriate since you want to display the contents of the string variable, instead of the name and not the datafolder it is in.

What I would do in this case is to populate the listbox with all the strings the moment it is created, but to create a link between string contents and datafolder name beforehand. So you need to have all the string variables for the list box, and then you need a way to map between from those contents to the datafolder. (side note: with this strategy you need to be able to guarantee that the contents of each stringvariable are unique.)

Here's a possible strategy:
- For establishing the connection between string contents and datafolder name, I would create a 2D text wave, where the first column is the contents of the string and the second column is the name of the datafolder. Once the user makes a selection you look for the string (= the key) in the first column and then take the second column as the datafolder. You could get away with using the index of the selection, too, but I like to look for content in case it becomes possible for the user to drag around the listbox entries in future versions of Igor.

- To make this 2D wave, you make a specific function. The outline of this function could be as follows:
Function MakeStringContentsDFWave()
    DFREF savDF = GetDataFolderDFR()
    DFREF myDataFolder = // some data folder where you store the measurement folders
   
    Make /T /N=(0,2) M_myMap
   
    string currentFolder
    for (i = 0;; i += 1)
        currentFolder = GetIndexedObjNameDFR(myDataFolder, 4, i)
        if (strlen(currentFolder) == 0)
            // no more folders remaining
            break
        endif
        SVAR myString = $(currentFolder + "nameOfTheStringVariable")
        Redimension /N=(DimSize(M_myMap, 0) + 1, -1) M_myMap
        M_myMap[DimSize(myMap, 0) - 1][0] = myString
        M_myMap[DimSize(myMap, 1) - 1][1] = currentFolder
    endfor

    SetDataFolder savDF
   
    return 0
End

This can become a bit more complex if you the data folders can be nested, but that can be sorted out using recursion.

Basically what you're doing here is making a map from key to content, where both types are strings. This is a very common data structure, and in Igor you can do this either with a text wave (when both key and content are strings), or with a string list. In the latter case you would use a global string variable instead of the wave, and access it using e.g. StringByKey. The advantage is that this carries over to numbers since NumberByKey exists, too.

- Next you make the listbox. The selectionwave for the listbox can be made using
wave /T M_myMap
Make /N=(DimSize(myMap, 0)) W_listWave
W_listWave = M_myMap[p][0]


- Then, when the user makes a selection, you take the selected text and look that up in the first column of M_myMap. The second column in the row with the matching index gets you the dataFolder.
Hello 741,

Thank you for your reply. For the moment I am using the row index of the listbox in order to retrieve the filename string content. This index corresponds to the datafolder name. Indeed, as you can see on the previous screenshot, I've chosen to name the datafolders '0', '1', '2' and so on.
Your solution would require some changes, since a lot of imported datasets will have the same name (e.g. matrix1.asc).
The "junction" table used to make this kind of relational database would then require an additionnal column : the filepath. Thus, we are sure that the identifier formed by the path + the file name is unique.
Anyway, thank you for this idea, I may need it if I decide to sort the files names in the listbox.
For the moment, I don't think that's crucial... but we never know, maybe I will encounter an unexpected behaviour which will force me to create this look-up table...(for instance, if I want to detect a dataset which is already imported, or if I want to remove it from the listbox)

Best Regards