-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathafni_picker
executable file
·185 lines (165 loc) · 5.06 KB
/
afni_picker
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/wish
# plugouts wrapper (set overlay/underlay)
# afni_select.tcl path/to/*nii.gz
# also launches
# afni -YESplugouts path/to/*nii.gz
set SELECTOR {rofi -dmenu}
package require Tk
proc linkorname {f} { if { [ file exist "$f" ] && ({link} eq [file type "$f"]) } { file readlink "$f"} else { return "$f" } }
proc bnames {list} {
set res {}
foreach e $list {
set real_name [linkorname $e]
# if file doesn't exist, it's probably an afni command, like:
# '-com' or 'SET_UNDERLAY ...'
if { [ file exist $real_name ] } {
lappend res [file tail $real_name]
}
}
set res
}
# for testing we can use noop instead of exec
proc noop {args} {}
set DRY exec
#set DRY noop
## globals
# widgets
listbox .lb
scrollbar .sb
label .note
array set COLORED_IDX {
SET_UNDERLAY -1
SET_OVERLAY -1
}
array set COLORS {
SET_UNDERLAY lightblue
SET_OVERLAY lightyellow
BOTH orange
VISITED lightgray
}
set PORT_BLOCK 0
## Funcs
proc list_files {argv} {
# try to mimic afni's file listing procedure
# launched without args, use cwd
# with directory (as singline input), list all inside directory
# otherwise explicit files
set narg [llength $argv]
if {$narg == 0} {
return [glob "*.nii" "*HEAD" "*.nii.gz"]
} elseif {$narg == 1 && [file isdirectory $argv]} {
return [glob "$argv/*nii.gz" "$argv/*nii" "$argv/*HEAD"]
} else {
return $argv
}
}
# launch afni
proc launch_afni {} {
variable DRY
variable PORT_BLOCK
$DRY afni -npb $PORT_BLOCK -YESplugouts {*}$::argv &
}
proc set_color {idx color} {
.lb itemconfigure $idx -background $color
.lb itemconfigure $idx -selectbackground $color
}
proc update_color {how idx} {
# change the color based on selected choice
# track state to change color back when new is selected
# if selection is same for both overlay and underlay
# use special color, and handling going back to only one
variable COLORED_IDX
variable COLORS
set other [expr {$how eq "SET_UNDERLAY" ? "SET_OVERLAY" : "SET_UNDERLAY"}]
if { $idx == $COLORED_IDX($other) } {
set_color $idx $COLORS(BOTH)
} else {
set_color $idx $COLORS($how)
}
if {$COLORED_IDX($how) > -1 && $idx != $COLORED_IDX($how)} {
# change color to visited unless the other is also set
# then set the color back to the other's (instead of both)
set restore_color $COLORS(VISITED)
if { $COLORED_IDX($how) == $COLORED_IDX($other) } {
set restore_color $COLORS($other)
}
set_color $COLORED_IDX($how) $restore_color
}
# update state
set COLORED_IDX($how) $idx
}
proc afni_plug {cmd} {
# send command(s) to afni
# use PORT_BLOCK to make sure we're driving the correct afni
variable PORT_BLOCK
variable DRY
#exec plugout_drive -npb $PORT_BLOCK -com [join $cmd "-com"] -quit &
exec plugout_drive -npb $PORT_BLOCK -com $cmd -quit &
}
proc select_img {how coord} {
# send e.g. 'SET_UNDERLAY mprage.nii.gz'
set idx [.lb index $coord]
set img [.lb get $idx]
update_color $how $idx
afni_plug "$how $img"
}
proc swap_over_under {} {
variable COLORED_IDX
set new_u $COLORED_IDX(SET_OVERLAY)
set new_o $COLORED_IDX(SET_UNDERLAY)
if {$new_u < 0 || $new_o < 0 } {return}
select_img SET_UNDERLAY $new_u
exec sleep 1
select_img SET_OVERLAY $new_o
}
proc fuzzy {how} {
# use e.g. rofi as a popup fuzzy selector
variable SELECTOR
set sel ""
set opts [.lb get 0 end]
catch {set sel [exec echo [join $opts \n] | {*}$SELECTOR]}
if {$sel ne ""} {
select_img $how [lsearch -exact $opts $sel]
}
}
## run
# basename of all inputs.
.lb insert 0 {*}[bnames [list_files $::argv]]
# block offset by number of afni's currently open
set PORT_BLOCK [exec sh -c {pgrep afni |wc -l}]
puts "# offset blocks by $PORT_BLOCK"
launch_afni
## bindings
# clicks
bind .lb <ButtonPress-1> { select_img SET_UNDERLAY @%x,%y}
bind .lb <ButtonPress-3> { select_img SET_OVERLAY @%x,%y}
# keys
bind .lb <slash> { fuzzy SET_UNDERLAY }
bind .lb <space> { fuzzy SET_UNDERLAY }
bind .lb <question> { fuzzy SET_UNDERLAY }
bind .lb <Return> { select_img SET_UNDERLAY [.lb curselection] }
bind .lb <Control-Return> { select_img SET_OVERLAY [.lb curselection] }
bind .lb <o> { afni_plug "SET_FUNC_VISIBLE +" }
bind .lb <O> { afni_plug "SET_FUNC_VISIBLE -" }
bind .lb <U> { swap_over_under }
bind .lb <P> { afni_plug "PURGE_MEMORY" }
bind .lb <j> { exec afni_atlas_jump & }
#bind .lb <J> { afni_plug "JUMP_BACK" } # afni doesn't have this driver
wm protocol . WM_DELETE_WINDOW { afni_plug QUIT; destroy .}
## widget config
.lb config -yscrollcommand {.sb set}
.sb config -command {.lb yview} -orient vertical
.note configure -text [join {
"/ ?: fuzzy search underlay/overlay"
"left, right click: set under/overlay"
"enter,C-enter: set under/overlay"
"o,O: overlay on/off"
"U: swap over/under"
"P: 'purge' (reload) all datasets"
"j: jump to region"
"& in afni: 3/#, 4/$, 5/% for splits"} \n ]
# display
# pack forget .lb .sb .note
pack .note -side top -fill x
pack .lb -side left -fill both -expand true
pack .sb -side right -fill y