Extracting contents of table's target cell?
As part of a programming project using Igor Pro v.8, I want to make sure that numeric data my user enters into a table (itself a subwindow within a panel), is properly bounded. I've created hook functions for the parent window and for the table subwindow and plan to detect when the user moves away from the target cell (ex. Enter or Return, Arrow Keys, etc). At that time, I want to read the content of the target cell and limit the entered value to within my [min, max] range.
In this circumstance, how can my code determine the contents of the target cell? In some circumstances it seems sufficient to use the value of the corresponding point in the wave represented by the column in the table, but in other circumstances, that scheme doesn't seem to work. For example: move to a cell already containing a value; enter a new numeric value; press Enter. In this case, the point in the wave does not yet (in the hook function), contain the new numeric value -- instead it contains the previous point.
I would've thought that GetSelection table, ... would be approp. function to read the data directly from the table itself, rather than the 'backing wave', but I haven't yet found a way to get the value of that target cell.
Any suggestions?
Thanks,
Bruce
I recommend that you take a different approach. Check all of the entered data regardless of what the target cell is.
That said, here is a first-crack attempt at doing what you asked for. Note the caveats explained in the comments and that I have tested this very briefly.
(However, by the time your hook is called, that target cell may have already been changed.)
// For convenience in testing GetSelectedTableNumericValue
"Print Selected Table Numeric Value/1", /Q, Print GetSelectedTableNumericValue("")
End
// This works only if the table contains only 1D waves and only if
// the data column from each wave is selected. If the index column for
// a wave is selected, it returns the corresponding data value.
Function GetSelectedTableNumericValue(tableName)
String tableName // "" for top table
if (strlen(tableName) == 0)
tableName = WinName(0, 2)
endif
if (WinType(tableName) != 2)
return NaN // Not a table
endif
GetSelection table, $tableName, 7
if (V_Flag == 0) // No selection?
return NaN // Should not happen
endif
// First selected table row, zero-based
Variable firstSelectedRow = V_startRow
// First selected table column, zero-based
Variable firstSelectedColumn = V_startCol
// Name of first selected column (e.g. wave0.d)
String columnName = StringFromList(0, S_Selection)
String wName // Strip, e.g., ".d" from column name to get wave name
SplitString/E="(.*)\..*$" columnName, wName
// Path to first selected wave's data folder
String dfPath = StringFromList(0, S_dataFolder)
String fullDFPath = dfPath + wName
Wave/Z w = $fullDFPath
if (!WaveExists(w))
return NaN // An empty cell is selected
endif
if (WaveType(w,1) != 1)
return NaN // Not a numeric wave
endif
Variable value = w[firstSelectedRow]
return value
End
August 16, 2018 at 01:26 pm - Permalink
Hi, and thanks for your feedback w/ sample code.
If I understand correctly, you're recommending that the code perform a bounds-check on each element in the wave, each time the return key is pressed. While that would be too slow for long waves, it could work for my application where the waves are likely to be short.
However, that approach wouldn't solve the problem that seems to arise when the user presses 'Enter' immediately after editing the value of a cell. In that case, my (quick) experiments suggest that the wave still contains the old (pre-editing) value, while the table's cell contains the new (post-editing) value. Thus checking the data in the wave appears unreliable as a scheme to immediately catch users' errors.
Please let me know if I should hook a different event to catch such changes, or if I've misunderstood something.
August 20, 2018 at 05:20 pm - Permalink
In reply to Hi, and thanks for your… by BRUCE
I would need to know exactly what you are doing in order to comment. If you will post a minimal, self-contained example that I can run and debug, I will look into this.
August 21, 2018 at 07:15 am - Permalink
That suggests that you are using a window hook function, and perhaps you are using the mouse-down event to catch the edited value. Try the mouse-up event.
August 21, 2018 at 09:44 am - Permalink
I don't see how a table hook helps with this. Below is a simple self-contained example. I get no event when I edit the value of a table cell.
STRUCT WMWinHookStruct &s
Variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
String message = ""
Wave tableHookWave
Variable value = tableHookWave[0]
switch(s.eventCode)
case 3: // Mouse down event
Printf "Mouse down event, value=%g\r", value
break
case 5: // Mouse up event
Printf "Mouse up event, value=%g\r", value
break
case 8: // Modified event - sent to graph and notebook windows only
Printf "Modified event, value=%g\r", value
break
endswitch
return hookResult // If non-zero, we handled event and Igor will ignore it.
End
Function DemoTableWindowHook()
DoWindow/F TableEventsTable // Does table exist?
if (V_flag == 0)
// Create table
Make/O/N=3 tableHookWave = p
Edit /N=TableEventsTable tableHookWave as "Table Events"
// Install hook
SetWindow TableEventsTable, hook(MyHook)=TableWindowHook
endif
End
August 21, 2018 at 10:24 am - Permalink
Here is an example using a dependency. It works but I don't know how to get the dependency to run just once when the wave is modified instead of once for every point.
DoWindow/F TableDependencyTable // Does table exist?
if (V_flag == 0)
// Create table
Make/O/N=3 tableDependencyWave = p
Edit /N=TableDependencyTable tableDependencyWave as "Table Dependency"
// Set dependency
SetFormula tableDependencyWave, "root:tableDependencyWave + CheckTableDependencyWave()"
endif
End
Function CheckTableDependencyWave()
// Print "In CheckTableDependencyWave"
Wave w = root:tableDependencyWave
// Print w
Variable numPoints = numpnts(w)
Variable i
for(i=0; i<numPoints; i+=1)
Variable val = w[i]
if (val<0 || val>9)
Printf "Point %d of tableDependencyWave is out of range - must be 0 to 9\r", i
endif
endfor
return 0
End
August 21, 2018 at 10:25 am - Permalink
Right. My comment wasn't well thought out; I was thinking of something else.
@BRUCE Are you using a window hook function? If so, better post some code because we can't figure out what you're doing :)
August 21, 2018 at 11:03 am - Permalink
Hi All,
Thanks for the feedback, and sorry for my slow return to this thread.
I've attached a .pxp file that I think will demonstrate the puzzle. Please see the comments at the top of the procedure window for more details.
Looking forward to your feedback,
bp
August 31, 2018 at 11:03 am - Permalink
Ah, I see the problem. You're right- when the Enter key event comes in, the wave hasn't actually been updated. Which is what you told us before :). But it took some digging to uncover the fact that you are using a window hook. Window hooks can be very tricky; in this case I think it simply doesn't give you what you need. Perhaps in a future version?
I think you can, with effort, achieve what you need using a Listbox control. It is difficult because a Listbox deals only in text, not numbers, so you have to prepare a text wave with the contents of the wave you want to edit translated into text.
On the other hand, Listbox controls are made for editing and it's not hard to find out the selection and the new text. Then you can vet the new text before finalizing the value to put into the real numeric wave.
August 31, 2018 at 04:45 pm - Permalink
You might want to look at ModifyTable entryMode. There are commands to check whether values are being edited and not (yet) accepted.
There isn't a way to get the text of the current entry line in Igor until Igor 8.02: ModifyTable entryMode sets S_Value beginning with Igor 8.02.
August 31, 2018 at 06:15 pm - Permalink
In reply to You might want to look at… by JimProuty
You could do it with a dependency if you create a one point dummy dependency wave:
wave w
// keep wave points within bounds
w=max(10*q,min(w, 10*(q+1)),0)
return 1
end
•edit tablewave
•make /n=1 dependencywave
•dependencywave:=doCheck(tablewave)
September 1, 2018 at 09:17 am - Permalink
In Igor 8.02, I added two things to help you: Event codes sent when the table entry area is completed or rejected, and a ModifyTable entryMode=num that always sets S_Value to the text in the table entry area.
You can use the event like this:
function Func_TableHook(STRUCT WMWinHookStruct &s, string &parentWindowName)
Variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
switch(s.eventCode)
case 24: // tableEntryAccepted in Igor 8.02+
Print s.eventName // "tableEntryAccepted"
Func_WorkPosTableBoundsCheck()
break
case 25: // tableEntryCancelled in Igor 8.02+
Print s.eventName // "tableEntryCancelled"
break
default:
break
endswitch
return hookResult
end
And you can use the ModifyTable entry mode like this:
Print "table entry text = \""+S_Value+"\""
September 11, 2018 at 01:02 pm - Permalink
Thanks for the suggestions, and the refinements in Igor v.8.02.
Hope to get back to this question later this week, after which I can give more specific feedback.
bp
September 11, 2018 at 02:59 pm - Permalink
The hints and revisions in v.8.02 seem to solve the puzzle. The attached .ipf file shows the current versions of the table's hook function & the bounds checker (filter).
The new 'tableEntryAccepted' event lets me easily pick up when the user clicks away from the cell they were editing. By detecting that event, the mouse-down event, and a few keydown events, I can now call the filter no matter how the user terminates their edit. The new 'tableEntryCancelled' event doesn't seem necessary (at least for now).
The new assignment of the cell's contents to S_Value lets my filter constrain the user's inputs to permitted values. I'm not sure if there's a way to write the filtered value directly back into the table, but writing the filtered value directly into the wave has seemed adequate so far.
Thanks again for the help,
bp
September 13, 2018 at 04:06 pm - Permalink
Writing a value into a wave displayed in a table is the same thing as writing "directly into the table". If your code does other things after changing the wave, and it's important for the table to immediately show the change in value, you may need to call DoUpdate.
September 13, 2018 at 04:33 pm - Permalink
Will keep that in mind. Thx.
September 13, 2018 at 04:55 pm - Permalink
Here's a very compact example of getting information about an edited table cell using a table hook and the new ModifyTable query:
Macro MakeTableHookExample()
make/O/N=(20,20) twod=p*q
make/O/N=20 short=gnoise(1)
Edit/N=Table0 short, twod
SetWindow kwTopWin,hook(myhook)=Func_TableHook
DoAlert 0, "Try editing any value, look at history output"
End
function Func_TableHook(STRUCT WMWinHookStruct &s)
Variable hookResult = 0 // 0 if we do not handle event, 1 if we handle it.
strswitch(s.eventName)
case "tableEntryAccepted":
ModifyTable/W=$s.winName entryMode=0 // query, result in S_Value
Print "accepted: "+S_Value
GetSelection table, $s.winName, 1+2+4
// 1 Sets V_startRow, V_startCol, V_endRow, V_endCol based on the selected cells in the table.
// The top/left cell, not including the Point column, is (0, 0).
// 2 Sets S_selection to a semicolon-separated list of column names.
// 4 Sets S_dataFolder to a semicolon-separated list of data folders, one for each column.
Print V_StartRow, V_StartCol, S_Selection, S_DataFolder
break
default:
break
endswitch
return hookResult
End
April 24, 2023 at 02:04 pm - Permalink
Thanks Jim -- that's some customer support!
Hoping to get back to this someday,
Bruce
May 4, 2023 at 01:00 pm - Permalink