
Auto-Syncing Checkbox States Using Dependent Waves in Igor Pro

guoqilin
Question
I have a panel with four checkboxes: `p` (master), `px`, `py`, and `pz`. The `p` checkbox acts as a **master checkbox**, which controls the others as follows:
- Master control: Checking/unchecking `p` should check/uncheck all dependent checkboxes (`px`, `py`, `pz`).
- Auto-update master:
- If any dependent checkbox is unchecked, `p` should uncheck.
- If all dependent checkboxes are checked, `p` should check.
My current implementation achieves this logic but uses a wave (`pDependentCheckBoxeValues`) to track the dependent checkbox states. However, this wave requires manual updates when the dependent checkboxes change.
How can I make `pDependentCheckBoxeValues` a _dependent wave_ that automatically updates when its source variables (`pxCheckBoxValue`, `pyCheckBoxValue`, `pzCheckBoxValue`) change, without explicitly rewriting it each time?
Current Code:
#pragma TextEncoding = "UTF-8" #pragma rtGlobals=3 // Use modern global access method and strict wave access #pragma DefaultTab={3,20,4} // Set default tab width in Igor Pro 9 and later Function Main() Initialize() Execute "OrbitalSelectionPanel()" End Function Initialize() Variable/G pCheckBoxValue = 0 Variable/G pxCheckBoxValue = 0 Variable/G pyCheckBoxValue = 0 Variable/G pzCheckBoxValue = 0 Make/O pDependentCheckBoxeValues = {pxCheckBoxValue, pyCheckBoxValue, pzCheckBoxValue} End Function pCheckBoxProc(cba) : CheckBoxControl STRUCT WMCheckboxAction &cba switch( cba.eventCode ) case 2: // mouse up Variable checked = cba.checked NVAR pxCheckBoxValue = root:pxCheckBoxValue NVAR pyCheckBoxValue = root:pyCheckBoxValue NVAR pzCheckBoxValue = root:pzCheckBoxValue WAVE pDependentCheckBoxeValues = pDependentCheckBoxeValues pxCheckBoxValue = checked pyCheckBoxValue = checked pzCheckBoxValue = checked pDependentCheckBoxeValues = {pxCheckBoxValue, pyCheckBoxValue, pzCheckBoxValue} break case -1: // control being killed break endswitch return 0 End Function pCheckBoxSync(cba) : CheckBoxControl STRUCT WMCheckboxAction &cba switch( cba.eventCode ) case 2: // mouse up Variable checked = cba.checked NVAR pxCheckBoxValue = pxCheckBoxValue NVAR pyCheckBoxValue = pyCheckBoxValue NVAR pzCheckBoxValue = pzCheckBoxValue NVAR pCheckBoxValue = pCheckBoxValue WAVE pDependentCheckBoxeValues = pDependentCheckBoxeValues pDependentCheckBoxeValues = {pxCheckBoxValue, pyCheckBoxValue, pzCheckBoxValue} pCheckBoxValue = sum(pDependentCheckBoxeValues) == numpnts(pDependentCheckBoxeValues) break case -1: // control being killed break endswitch return 0 End Window OrbitalSelectionPanel() : Panel PauseUpdate; Silent 1 // building window... Variable CheckBoxwidth = 40.00 Variable CheckBoxheight = 12.00 Variable VerticalDistance = 20.00 Variable HorizontalDistance = 40.00; Variable XPosition = 20.00 Variable YPosition = 20.00 NewPanel /W=(500.0,300.0,800.0,400.0) SetDrawLayer UserBack CheckBox pCheckBox, pos={XPosition+0*HorizontalDistance,YPosition+1*VerticalDistance},size={CheckBoxwidth,CheckBoxheight},proc=pCheckBoxProc,title="p", variable=pCheckBoxValue,help={"Select all p orbitals"} CheckBox pxCheckBox,pos={XPosition+1*HorizontalDistance,YPosition+1*VerticalDistance},size={CheckBoxwidth,CheckBoxheight},proc=pCheckBoxSync,title="px",variable=pxCheckBoxValue CheckBox pyCheckBox,pos={XPosition+2*HorizontalDistance,YPosition+1*VerticalDistance},size={CheckBoxwidth,CheckBoxheight},proc=pCheckBoxSync,title="py",variable=pyCheckBoxValue CheckBox pzCheckBox,pos={XPosition+3*HorizontalDistance,YPosition+1*VerticalDistance},size={CheckBoxwidth,CheckBoxheight},proc=pCheckBoxSync,title="pz",variable=pzCheckBoxValue EndMacro
First, I would avoid using global variables and especially waves (which are definitely visible to users) to store control variables. This is not necessary unless you explicitly use the variables / waves for some other task unrelated to the panel. Use
ControlInfo/W=PanelName CtrlName
to inquire the state of each checkbox instead. Call
CheckBox CrtlName ,win=PanelName ,value=setTheValue
to set the value in the panel.
Otherwise, I find no problem with your approach. pCheckBoxSync() is exactly the way to go about this and to implement your conditions. Just set the value of each Checkbox within the mouse-up even while doing (p was pressed => check/uncheck all px,py,pz) and (all px,py,pz active / inactive => adjust p). I think doing it 'automatically' (whatever that means) will just lead to much pain down the road. To discern which of the check-boxes was pressed you can...
June 17, 2025 at 03:57 am - Permalink
Hi chozo,
Thank you for taking the time to review my implementation and for your insightful suggestions! Your advice on avoiding global variables/waves and directly querying/setting control states via
ControlInfo
andCheckBox
commands was exactly what I needed to streamline the code. I’ve refactored the solution to:Eliminate global variables and use panel control states directly.
Simplify logic by unifying checkbox handlers and leveraging
WhichListItem()
for dynamic checks.Improve robustness by enforcing panel-name constants and consistent loops.
Your point about avoiding "automatic" dependencies (like wave-based tracking) was particularly valuable—it led me to a cleaner, more maintainable approach. The final version now handles all sync logic within
pCheckBoxSync()
, triggered by a singleCheckBoxControl
function.I’ve shared the refined code below in case it’s useful for others. Thanks again for your guidance—it’s greatly appreciated!
June 17, 2025 at 08:03 am - Permalink
Great that it worked for you! :-) Maybe one more thing you want to change, especially if you are sharing your code with other users: Avoid giving generic names to functions and global constants such as "panelName" and "Main". As soon as another function / constant / wave with this name exists anywhere in your current Igor session you will get an error. You may use the 'static' prefix for functions / constants which are never exposed to users to prevent this. Here is your code boiled down to the essentials:
Or if you have Igor 9:
June 17, 2025 at 08:20 pm - Permalink