1#!/usr/local/bin/python3.8
2
3# python3 status: compatible
4
5# basically, a GUI to write an afni_proc.py command
6
7import sys, os, math
8
9# system libraries : test, then import as local symbols
10from afnipy import module_test_lib
11testlibs = ['signal', 'PyQt4']
12if module_test_lib.num_import_failures(testlibs): sys.exit(1)
13import signal
14
15# allow ctrl-c its default behavior
16signal.signal(signal.SIGINT, signal.SIG_DFL)
17
18from PyQt4 import QtCore, QtGui
19
20from afnipy import afni_base as BASE
21from afnipy import afni_util as UTIL
22from afnipy import lib_subjects as SUBJ
23from afnipy import lib_vars_object as VO
24from afnipy import lib_uber_subject as USUBJ
25from afnipy import lib_qt_gui as QLIB
26
27# allow users to play with style
28g_styles = ["windows", "motif", "cde", "plastique", "cleanlooks"]
29g_style_index = 0
30g_style_index_def = 4
31
32g_spacing = 3
33g_glayout_spacing = 2
34g_grid_spacing = 6
35
36g_LineEdittype = None                   # set this type later
37
38
39# ======================================================================
40# main module class
41class SingleSubjectWindow(QtGui.QMainWindow):
42   """class for a single subject main window
43
44         parent         parent Widget, can be None if this is main Widget
45         subj_vars      VarsObject for init of gui fields
46         ctrl_vars      VarsObject for init of control vars
47         set_sdir       upon GUI exec, set cvars.subj_dir bases on sid
48         verb           verbose level, default of 1
49   """
50
51   def __init__(self, parent=None, subj_vars=None, ctrl_vars=None,
52                set_sdir=0, verb=-1):
53      super(SingleSubjectWindow, self).__init__(parent)
54
55      # ------------------------------
56      # init main vars structs
57      self.verb   = verb
58      self.apsubj = None        # AP_Subject class element
59      self.gvars  = VO.VarsObject('uber_subject gui vars')
60
61      # initialize the subject variables to defaults, update at the end
62      self.svars  = USUBJ.g_sdef_strs.copy('subject vars')
63      self.cvars  = USUBJ.g_cdef_strs.copy('control vars')
64      if self.verb < 0: self.verb = int(self.cvars.verb)
65      self.set_sdir = set_sdir
66      if ctrl_vars and set_sdir:
67          if ctrl_vars.is_not_empty('subj_dir'):
68              if self.verb:
69                 print('++ have passed subj_dir %s, keeping it' \
70                       % ctrl_vars.val('subj_dir'))
71              set_sdir = 0
72              self.set_sdir = 0
73
74      # ------------------------------
75      # L1 - main menubar and layout
76      self.add_menu_bar()
77      self.add_tool_bar()
78      self.add_status_bar()
79      self.gvars.Wcentral = QtGui.QWidget()
80      mainlayout = QtGui.QVBoxLayout(self.gvars.Wcentral)
81
82      # ------------------------------
83      # L2 - make Group Box and Vertical Layout; add to main Layout
84      self.make_l2_widgets()
85
86      # ------------------------------
87      # L3 - fill m2_vlayout with group boxes
88      self.make_l3_group_boxes()
89
90      # ------------------------------------------------------------
91      # finish level 2 and then level 1
92      mainlayout.addWidget(self.gvars.Widget_ID)
93      mainlayout.addWidget(self.gvars.m2_scroll)
94
95      # ------------------------------
96      # save this for last to ensure it is all visible
97      self.gvars.m2_scroll.setWidget(self.gvars.m2_gbox_inputs)
98
99      self.gvars.Wcentral.setMinimumSize(200, 300)
100      self.gvars.Wcentral.resize(400, 800)
101      self.gvars.Wcentral.setLayout(mainlayout)
102      self.setCentralWidget(self.gvars.Wcentral)
103
104      self.update_all_gbox_styles()
105
106      self.make_extra_widgets()
107
108      self.gvars.style = g_styles[g_style_index_def]
109
110      # widgets are done, so apply pass subject vars
111      self.reset_vars(svars=subj_vars, cvars=ctrl_vars,
112                      set_sdir=set_sdir, verb=verb)
113
114      # ap_status : 0 = must create ap command, 1 = have ap, need proc script,
115      #             2 = have proc script, ready to execute, 3 = executing/ed
116      # (meaning user must have first generated (and viewed) the command)
117      self.gvars.ap_status = 0
118
119      if self.verb > 1: print('-- finished Single Subject Dialog setup')
120
121   def reset_vars(self, svars=None, cvars=None, set_sdir=0, verb=-1):
122      """replace old svars/cvars with new"""
123
124      # get verb from one of 4 places: passed, cvars, self, init to 1
125      vv = -1
126      if cvars != None:
127         vv = cvars.val('verb')
128         if vv == None: vv = -1
129         else:
130            try: vv = int(vv)
131            except:
132               '** reset vars bad verb %s %s' % (vv, type(vv))
133               vv = -1
134      if   verb >= 0:     self.verb = verb
135      elif vv   >= 0:     self.verb = vv
136      elif self.verb > 0: pass                  # leave unchanged
137      else:               self.verb = 1
138
139      self.set_sdir = set_sdir
140
141      del(self.svars)
142      del(self.cvars)
143
144      self.svars = USUBJ.g_sdef_strs.copy()
145      self.cvars = USUBJ.g_cdef_strs.copy()
146
147      self.apply_svars(svars)
148      self.apply_cvars(cvars)
149
150      # since might not come from cvars, cvars are all strings
151      self.set_cvar('verb', str(self.verb))
152
153   def make_l2_widgets(self):
154      """create 'general subject info' box and scroll area for the
155         rest of the inputs
156
157         QGroupBox(general)
158         QScrollArea
159            QGroupBox(input data and options) with VBox layout
160               elsewhere:
161                  QGroupBox(anat)
162                  QGroupBox(epi)
163                  QGroupBox(stim)
164                  ...
165      """
166
167      self.make_subj_group_line()  # sid/gid
168
169      self.gvars.m2_scroll = QtGui.QScrollArea()
170      self.gvars.m2_scroll.setWidgetResizable(True)
171
172      # create a containing GroupBox for everything in m2_scroll
173      gbox = self.get_styled_group_box("input data and options")
174
175      # the layout for the 'input datasets' QGroupBox is vertical
176      self.gvars.m2_vlayout = QtGui.QVBoxLayout(gbox)
177      gbox.setLayout(self.gvars.m2_vlayout)
178      self.gvars.m2_vlayout.addSpacing(2)
179
180      self.gvars.m2_gbox_inputs = gbox
181
182   def update_all_gbox_styles(self):
183      for gbox in [ self.gvars.gbox_anat, self.gvars.gbox_epi,
184                    self.gvars.gbox_stim ]:
185         self.update_gbox_style(gbox)
186
187   def update_gbox_style(self, gbox):
188
189      # rcr - maybe consider these later
190
191      # color:                      font
192      # background-color:           entire box, and child widgets
193      # selection-color:            font of selected characters
194      # selection-background-color: background of selected characters
195
196      return
197
198   def get_styled_group_box(self, title):
199
200      gbox = QtGui.QGroupBox(title)
201
202      # set box color
203      color = QtGui.QColor('blue').light(50)
204      palette = QtGui.QPalette(gbox.palette())
205      palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Mid, color)
206      gbox.setPalette(palette)
207
208      return gbox
209
210   def make_subj_group_line(self):
211      """add a single line at the top level for subject and group IDs
212
213         create Widget_ID to add to top level
214
215         for controlling vars sid, gid
216      """
217
218      # --------------------------------------------------
219      # Widget/HBox: subject and group ID fields
220      bwidget = QtGui.QWidget()
221      layout = QtGui.QHBoxLayout()
222
223      # add subject ID stuff, init with sid
224      label = QLIB.make_label("subject ID",
225                        tip='identifier to use in file and directory names')
226      self.gvars.Line_sid = QtGui.QLineEdit()
227      self.gvars.Line_sid.setText(self.svars.sid)
228      layout.addWidget(label)
229      layout.addWidget(self.gvars.Line_sid)
230
231      # add group ID stuff, init with sid
232      label = QLIB.make_label("group ID",
233                        tip='identifier to use in file and directory names')
234      self.gvars.Line_gid = QtGui.QLineEdit()
235      self.gvars.Line_gid.setText(self.svars.gid)
236      layout.addWidget(label)
237      layout.addWidget(self.gvars.Line_gid)
238
239      # note callbacks for buttons
240      # ** cannot use new method on Ubuntu 9.04:
241      #    self.gvars.Line_sid.editingFinished.connect(self.CB_line_text)
242      self.connect(self.gvars.Line_sid, QtCore.SIGNAL('editingFinished()'),
243                   self.CB_line_text)
244      self.connect(self.gvars.Line_gid, QtCore.SIGNAL('editingFinished()'),
245                   self.CB_line_text)
246
247      # and set layout
248      layout.setMargin(g_spacing)
249      bwidget.setLayout(layout)
250
251      # --------------------------------------------------
252      # and put main widgets into main VBox layout
253      self.gvars.Widget_ID = bwidget
254
255      return
256
257   def make_basic_gbox(self, title):
258      """make gbox with vars of frame and frame_layout
259         - to be called at top of group_box_XXX
260      """
261
262      gbox = self.get_styled_group_box(title)
263
264      # put frame inside gbox, which we can hide via toggled button
265      frame = QtGui.QFrame(gbox)
266      frame.setFrameShape(QtGui.QFrame.NoFrame)
267      gbox.frame = frame
268      self.init_gbox_viewable(gbox, True)       # default to viewable
269      # gbox.toggled.connect(self.gbox_toggle_frame)
270      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
271
272      layout = QtGui.QVBoxLayout(frame)
273
274      gbox.frame_layout = layout
275
276      return gbox
277
278   def set_basic_gbox_layout(self, gbox):
279      """set layouts for basic gbox
280         - to be called at bottom of group_box_XXX
281         - matches make_basic_gbox
282      """
283
284      glayout = QtGui.QVBoxLayout(gbox)
285
286      gbox.frame_layout.setMargin(g_spacing)
287      gbox.frame_layout.setSpacing(g_glayout_spacing)
288      gbox.frame.setLayout(gbox.frame_layout)
289      glayout.addWidget(gbox.frame)
290      glayout.setSpacing(g_spacing)
291      glayout.setMargin(g_glayout_spacing)
292      gbox.setLayout(glayout)
293
294   def group_box_analysis(self):
295      """create a group box with a VBox layout:
296            HBox: Label(anal type) Chooser() Label(domain) Chooser() but(Apply)
297                  Label(process blocks) LineEdit()
298
299         for controlling sujbect vars: anal_type, anal_domain
300                                       blocks
301
302          ** no longer a group box
303             -> just leave a line for subj/group
304             -> move general analysis info down to input data and options
305      """
306
307      # make gbox with frame layout
308      gbox = self.make_basic_gbox("analysis initialization")
309      self.init_gbox_viewable(gbox, False)      # default to hidden
310      flayout = gbox.frame_layout
311
312      # --------------------------------------------------
313      # analysis type HBox for type and domain
314
315      bwidget = QtGui.QWidget()
316      layout = QtGui.QHBoxLayout()
317
318      # add analysis type
319      label = QLIB.make_label("type",
320                tip='analysis type to perfom: task or resting state')
321      pbut = QLIB.create_menu_button(bwidget, self.svars.anal_type,
322                USUBJ.g_def_anal_types, call_back=self.CB_gbox_PushB)
323      self.gvars.PushB_anal_type = pbut
324      layout.addWidget(label)
325      layout.addWidget(pbut)
326
327      # add analysis domain
328      layout.addStretch()
329      label = QLIB.make_label("domain",
330                tip='data domain for analysis')
331      pbut = QLIB.create_menu_button(bwidget, self.svars.anal_domain,
332                USUBJ.g_def_anal_domains, call_back=self.CB_gbox_PushB)
333      self.gvars.PushB_anal_domain = pbut
334      layout.addWidget(label)
335      layout.addWidget(pbut)
336
337      # add Apply button
338      layout.addStretch()
339      pb = QLIB.make_button("APPLY",
340                tip="set defaults that differ across types and domains",
341                cb=self.CB_gbox_PushB)
342      pb.setIcon(self.style().standardIcon(QtGui.QStyle.SP_MediaPlay))
343      layout.addWidget(pb)
344      self.gvars.PushB_anal_apply = pb
345
346      # add help button
347      layout.addStretch()
348      pb = QLIB.make_button("help",
349                tip="analysis initialization: type and data domain",
350                cb=self.CB_gbox_PushB)
351      pb.setIcon(self.style().standardIcon(QtGui.QStyle.SP_MessageBoxQuestion))
352      layout.addWidget(pb)
353      self.gvars.PushB_anal_help = pb
354
355      bwidget.setLayout(layout)
356
357      # and finally, add current widget to frame layout
358      flayout.addWidget(bwidget)
359
360      # --------------------------------------------------
361      # processing blocks HBox
362      bwidget = QLIB.create_label_lineedit_widget('processing blocks',
363                        ltip='list of processing blocks in analysis',
364                        etext=' '.join(self.svars.val('blocks')),
365                        ecb=self.CB_line_text)
366      self.gvars.Line_blocks = bwidget.LineEdit
367      flayout.addWidget(bwidget)
368
369      # --------------------------------------------------
370      # and put main widgets into main VBox layout
371      self.set_basic_gbox_layout(gbox)
372
373      return gbox
374
375   def CB_line_text(self):
376      """call-back for text updates in the level 3 gbox"""
377      obj = self.sender()
378      if obj == self.gvars.Line_sid:
379         self.update_textLine_check(obj, obj.text(), 'sid', 'subject ID',
380                                    QLIB.valid_as_identifier)
381      elif obj == self.gvars.Line_gid:
382         self.update_textLine_check(obj, obj.text(), 'gid', 'group ID',
383                                    QLIB.valid_as_identifier)
384      elif obj == self.gvars.Line_blocks:
385         self.set_blocks(str(obj.text()))
386      elif obj == self.gvars.Line_anat:
387         self.update_textLine_check(obj, obj.text(), 'anat', 'anatomical dset',
388                                    QLIB.valid_as_filepath)
389      elif obj == self.gvars.Line_apply_basis:
390         self.update_basis_function(obj.text())
391      elif obj == self.gvars.Line_apply_stype:
392         self.update_stim_type(obj.text())
393
394      elif obj == self.gvars.Line_tcat_nfirst:
395         self.update_textLine_check(obj, obj.text(), 'tcat_nfirst',
396                                    'first TRs to remove', QLIB.valid_as_int)
397
398      # maybe we need to write a valid_in_list validator...
399      elif obj == self.gvars.Line_volreg_base:
400         text = str(obj.text())
401         if text == '': text = USUBJ.g_def_vreg_base
402         if text in USUBJ.g_vreg_base_list:
403            self.set_svar('volreg_base', text)
404         else: # reset to previous value
405            self.gvars.Line_volreg_base.setText(self.svars.volreg_base)
406            QLIB.guiWarning("Error: invalid volreg base",
407                            "base '%s' not in %s\n\n" \
408                            % (text, ', '.join(USUBJ.g_vreg_base_list)), obj)
409
410      elif obj == self.gvars.Line_blur_size:
411         self.update_textLine_check(obj, obj.text(), 'blur_size',
412                                    'FWHM blur size', QLIB.valid_as_float)
413
414      elif obj == self.gvars.Line_motion_limit:
415         self.update_textLine_check(obj, obj.text(), 'motion_limit',
416                                    'motion censor limit', QLIB.valid_as_float)
417
418      elif obj == self.gvars.Line_outlier_limit:
419         self.update_textLine_check(obj, obj.text(), 'outlier_limit',
420                                 'outlier fraction limit', QLIB.valid_as_float)
421
422      elif obj == self.gvars.Line_regress_jobs:
423         self.update_textLine_check(obj, obj.text(), 'regress_jobs',
424                                 'CPU jobs in regression', QLIB.valid_as_int)
425
426      elif obj == self.gvars.Line_regress_GOFORIT:
427         self.update_textLine_check(obj, obj.text(), 'regress_GOFORIT',
428                                 'GOFORIT warning override', QLIB.valid_as_int)
429
430      elif obj == self.gvars.Line_regress_bandpass:
431         self.set_bandpass(str(obj.text()))
432
433      elif obj == self.gvars.Line_align_cost:
434         text = str(obj.text())
435         if text == '': text = USUBJ.g_def_align_cost
436         if text in USUBJ.g_align_cost_list:
437            self.set_svar('align_cost', text)
438         else: # reset to previous value
439            # keep self.gvars.Line_align_cost.setText(self.svars.align_cost)
440            self.set_svar('align_cost', text)  # apply anyway
441            QLIB.guiWarning("Warning: unknown alignment cost function",
442                            "cost '%s' not in %s\n\n" \
443                            % (text, ', '.join(USUBJ.g_align_cost_list)), obj)
444
445      elif obj == self.gvars.Line_tlrc_base:
446         text = str(obj.text())
447         if text == '': text = USUBJ.g_def_tlrc_base
448         if text in USUBJ.g_tlrc_base_list:
449            self.set_svar('tlrc_base', text)
450         else: # reset to previous value
451            # keep self.gvars.Line_tlrc_base.setText(self.svars.tlrc_base)
452            self.set_svar('tlrc_base', text)  # apply anyway
453            QLIB.guiWarning("Warning: unknown template",
454                            "tlrc_base '%s' not in %s\n\n" \
455                            % (text, ', '.join(USUBJ.g_tlrc_base_list)), obj)
456
457      else: print('** CB_line_text: unknown sender')
458
459   def set_blocks(self, bstring):
460      blist = bstring.split()
461      if UTIL.lists_are_same(blist, self.svars.blocks): return
462
463      if len(blist) == 0: return self.apply_svar_in_gui('blocks')
464
465      # something to do
466      errstr = ''
467      for block in blist:
468         if not block in USUBJ.g_def_blocks_all:
469            errstr += "** invalid processing block: %s\n" % block
470      if errstr != '':
471         QLIB.guiError('Error', errstr+'\nresetting to previous blocks...',self)
472         return self.apply_svar_in_gui('blocks')
473
474      self.set_svar('blocks', blist)
475
476   def set_bandpass(self, bstring):
477      flist = UTIL.string_to_float_list(bstring)
478      if bstring == None:
479         self.set_svar('regress_bandpass', [])
480         return
481      if flist == None:
482         QLIB.guiError('Error',
483                       "** invalid bandpass values\n"   \
484                       "   (2 floats are required)\n\n" \
485                       "   e.g. 0.01 0.1\n\n"           \
486                       "   resetting to previous...",
487                       self)
488         bpstr = ' '.join(self.svars.val('regress_bandpass'))
489         self.gvars.Line_regress_bandpass.setText(bpstr)
490         return
491
492      if len(flist) == 0: self.set_svar('regress_bandpass', [])
493      elif len(flist) == 2: self.set_svar('regress_bandpass', bstring.split())
494      else:
495         QLIB.guiError('Error',
496                       "** invalid bandpass values\n"           \
497                       "   (exactly 2 floats are required)\n\n" \
498                       "   e.g. 0.01 0.1\n\n"                   \
499                       "   resetting to previous...",
500                       self)
501         bpstr = ' '.join(self.svars.val('regress_bandpass'))
502         self.gvars.Line_regress_bandpass.setText(bpstr)
503
504   def make_l3_group_boxes(self):
505      """create anat, EPI, stim, etc. group boxes, and add to m2_vlayout"""
506
507      self.gvars.gbox_analysis = self.group_box_analysis()
508      self.gvars.gbox_anat = self.group_box_anat()
509      self.gvars.gbox_epi  = self.group_box_epi()
510      self.gvars.gbox_stim = self.group_box_stim()
511      self.gvars.gbox_gltsym   = self.group_box_gltsym()
512      self.gvars.gbox_expected = self.group_box_expected()
513      self.gvars.gbox_regress  = self.group_box_regress_opts()
514      self.gvars.gbox_align    = self.group_box_align()
515      self.gvars.gbox_tlrc     = self.group_box_tlrc()
516
517      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_analysis)
518      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_anat)
519      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_epi)
520      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_stim)
521      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_gltsym)
522      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_expected)
523      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_regress)
524      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_align)
525      self.gvars.m2_vlayout.addWidget(self.gvars.gbox_tlrc)
526
527   def group_box_align(self):
528      """create a group box with a VBox layout:
529            align_giant_move, align_cost,
530            anat_has_skull, tlrc_base, tlrc_ok_maxite
531            (rcr - later: maybe general align_opts_aea, tlrc_opts_at)
532      """
533
534      gbox = self.get_styled_group_box("extra align options")
535
536      # put frame inside gbox, which we can hide via toggled button
537      glayout = QtGui.QVBoxLayout(gbox)
538      frame = QtGui.QFrame(gbox)
539      frame.setFrameShape(QtGui.QFrame.NoFrame)
540      gbox.frame = frame
541      self.init_gbox_viewable(gbox, False)      # default to hidden
542      # gbox.toggled.connect(self.gbox_toggle_frame)
543      # no QtCore.SIGNAL('toggled()')?
544      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
545
546      layout = QtGui.QGridLayout(frame)         # now a child of frame
547      voffset = 0                               # vertical position in layout
548
549      # --------------------------------------------------
550      # align_cost, with chooser
551      label = QtGui.QLabel("align: cost function")
552      label.setStatusTip("cost funtion for aligning EPI to anat")
553      LE = QtGui.QLineEdit()
554      # choose button
555      blist = ['cost: %s' % cost for cost in USUBJ.g_align_cost_list]
556      pbut = QLIB.create_menu_button(frame, "choose", blist,
557                                     call_back=self.CB_gbox_PushB)
558      LE.setText(self.svars.align_cost)
559      self.connect(LE, QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
560
561      layout.addWidget(label, voffset, 0)
562      layout.addWidget(pbut, voffset, 1)
563      layout.addWidget(LE, voffset, 2)
564      self.gvars.Line_align_cost = LE
565      voffset += 1
566
567      # --------------------------------------------------
568      # checkbox: giant_move
569      cbox = QtGui.QCheckBox("align: use giant_move")
570      cbox.setStatusTip("use -giant_move in align_epi_anat.py")
571      cbox.setChecked(self.svars.align_giant_move=='yes')
572      # cbox.clicked.connect(self.CB_checkbox)
573      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
574
575      layout.addWidget(cbox, voffset, 0)
576      gbox.checkBox_align_giant_move = cbox
577      voffset += 1
578
579      # --------------------------------------------------
580      # and finish group box
581      layout.setMargin(g_spacing)
582      layout.setSpacing(g_spacing)
583      glayout.addWidget(frame)
584      glayout.setSpacing(g_glayout_spacing)
585      gbox.setLayout(glayout)
586      return gbox
587
588   def group_box_tlrc(self):
589      """create a group box with a VBox layout:
590      """
591
592      gbox = self.get_styled_group_box("extra tlrc options")
593
594      # put frame inside gbox, which we can hide via toggled button
595      glayout = QtGui.QVBoxLayout(gbox)
596      frame = QtGui.QFrame(gbox)
597      frame.setFrameShape(QtGui.QFrame.NoFrame)
598      gbox.frame = frame
599      self.init_gbox_viewable(gbox, False)      # default to hidden
600      # gbox.toggled.connect(self.gbox_toggle_frame)
601      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
602
603      layout = QtGui.QGridLayout(frame)         # now a child of frame
604      voffset = 0                               # vertical position in layout
605
606      # --------------------------------------------------
607      # tlrc_base, with chooser
608      label = QtGui.QLabel("tlrc: template")
609      label.setStatusTip("anatomical group registration template")
610      LE = QtGui.QLineEdit()
611      # choose button
612      blist = ['template: %s' % tt for tt in USUBJ.g_tlrc_base_list]
613      pbut = QLIB.create_menu_button(frame, "choose", blist,
614                                     call_back=self.CB_gbox_PushB)
615      LE.setText(self.svars.tlrc_base)
616      self.connect(LE, QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
617
618      layout.addWidget(label, voffset, 0)
619      layout.addWidget(pbut, voffset, 1)
620      layout.addWidget(LE, voffset, 2)
621      self.gvars.Line_tlrc_base = LE
622      voffset += 1
623
624      # --------------------------------------------------
625      # checkbox: anat_has_skull
626      #cbox = QtGui.QCheckBox("anat has skull")
627      #cbox.setStatusTip("check if input anat has skull, clear if not")
628      #cbox.setChecked(self.svars.anat_has_skull=='yes')
629      # cbox.clicked.connect(self.CB_checkbox)
630      #self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
631
632      #layout.addWidget(cbox, voffset, 0)
633      #gbox.checkBox_anat_has_skull = cbox
634      #voffset += 1
635
636      # --------------------------------------------------
637      # checkbox: OK maxite
638      cbox = QtGui.QCheckBox("tlrc: OK maxite")
639      cbox.setStatusTip("allow max iterations in @auto_tlrc skull strip")
640      cbox.setChecked(self.svars.tlrc_ok_maxite=='yes')
641      # cbox.clicked.connect(self.CB_checkbox)
642      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
643
644      layout.addWidget(cbox, voffset, 0)
645      gbox.checkBox_tlrc_ok_maxite = cbox
646      voffset += 1
647
648      # --------------------------------------------------
649      # and finish group box
650      layout.setMargin(g_spacing)
651      layout.setSpacing(g_spacing)
652      glayout.addWidget(frame)
653      glayout.setSpacing(g_glayout_spacing)
654      gbox.setLayout(glayout)
655      return gbox
656
657   def group_box_regress_opts(self):
658      """create a group box with a VBox layout:
659         for controlling sujbect vars:
660            outlier_limit, regress_jobs, regress_GOFORIT, compute_fitts,
661            reml_exec, run_clustsim, regress_opts_3dD
662      """
663
664      gbox = self.get_styled_group_box("extra regress options")
665
666      # put frame inside gbox, which we can hide via toggled button
667      glayout = QtGui.QVBoxLayout(gbox)
668      frame = QtGui.QFrame(gbox)
669      frame.setFrameShape(QtGui.QFrame.NoFrame)
670      gbox.frame = frame
671      self.init_gbox_viewable(gbox, False)      # default to hidden
672      # gbox.toggled.connect(self.gbox_toggle_frame)
673      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
674
675      layout = QtGui.QGridLayout(frame)         # now a child of frame
676      lineno = 0
677
678      # --------------------------------------------------
679
680      # rcr - add help buttons for expected and extra regress options
681
682      # outlier_limit
683      label = QtGui.QLabel("outlier censor limit (per TR)")
684      label.setStatusTip("censor TRs exceeding this fraction (range [0,1])")
685      self.gvars.Line_outlier_limit = QtGui.QLineEdit()
686      self.gvars.Line_outlier_limit.setText(self.svars.outlier_limit)
687      self.connect(self.gvars.Line_outlier_limit,
688                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
689
690      layout.addWidget(label, lineno, 0)
691      layout.addWidget(self.gvars.Line_outlier_limit, lineno, 1)
692      lineno += 1
693
694      # jobs
695      label = QtGui.QLabel("jobs for regression (num CPUs)")
696      label.setStatusTip("number of CPUs to use in 3dDeconvolve")
697      self.gvars.Line_regress_jobs = QtGui.QLineEdit()
698      self.gvars.Line_regress_jobs.setText(self.svars.regress_jobs)
699      self.connect(self.gvars.Line_regress_jobs,
700                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
701
702      layout.addWidget(label, lineno, 0)
703      layout.addWidget(self.gvars.Line_regress_jobs, lineno, 1)
704      lineno += 1
705
706      # GOFORIT
707      label = QtGui.QLabel("GOFORIT level (override 3dD warnings)")
708      label.setStatusTip("number of 3dDeconvolve warnings to override")
709      self.gvars.Line_regress_GOFORIT = QtGui.QLineEdit()
710      self.gvars.Line_regress_GOFORIT.setText(self.svars.regress_GOFORIT)
711      self.connect(self.gvars.Line_regress_GOFORIT,
712                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
713
714      layout.addWidget(label, lineno, 0)
715      layout.addWidget(self.gvars.Line_regress_GOFORIT, lineno, 1)
716      lineno += 1
717
718      # regress bandpass (entry takes 2 values)
719      label = QLIB.make_label("bandpass in regression (2 floats)",
720                tip="give bottom/top bandpass frequencies (e.g. 0.01 0.1)")
721      if self.svars.val_len('regress_bandpass') == 2:
722         bptext = ' '.join(self.svars.val('regress_bandpass'))
723      else: bptext = ''
724      self.gvars.Line_regress_bandpass = \
725              QLIB.make_line(bptext, cb=self.CB_line_text)
726
727      layout.addWidget(label, lineno, 0)
728      layout.addWidget(self.gvars.Line_regress_bandpass, lineno, 1)
729      lineno += 1
730
731      # checkbox : regress_mot_deriv
732      cbox = QtGui.QCheckBox("regress motion derivatives")
733      cbox.setStatusTip("regress motion derivatives (in addition to motion)")
734      cbox.setChecked(self.svars.regress_mot_deriv=='yes')
735      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
736      layout.addWidget(cbox, lineno, 0)
737      lineno += 1
738      gbox.checkBox_mot_deriv = cbox
739
740      # checkbox : reml_exec
741      cbox = QtGui.QCheckBox("execute 3dREMLfit")
742      cbox.setStatusTip("execute 3dREMLfit regression script")
743      cbox.setChecked(self.svars.reml_exec=='yes')
744      # cbox.clicked.connect(self.CB_checkbox)
745      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
746      layout.addWidget(cbox, lineno, 0)
747      lineno += 1
748      gbox.checkBox_reml_exec = cbox
749
750      # checkbox : run_clustsim
751      cbox = QtGui.QCheckBox("run cluster simulation")
752      cbox.setStatusTip("store 3dClustSim table in stats results")
753      cbox.setChecked(self.svars.run_clustsim=='yes')
754      # cbox.clicked.connect(self.CB_checkbox)
755      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
756      layout.addWidget(cbox, lineno, 0)
757      lineno += 1
758      gbox.checkBox_run_clustsim = cbox
759
760      # checkbox : compute_fitts
761      cbox = QtGui.QCheckBox("compute fitts dataset")
762      cbox.setStatusTip("save RAM in 3dD by computing fitts afterwards")
763      cbox.setChecked(self.svars.compute_fitts=='yes')
764      # cbox.clicked.connect(self.CB_checkbox)
765      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
766      layout.addWidget(cbox, lineno, 0)
767      lineno += 1
768      gbox.checkBox_compute_fitts = cbox
769
770      # --------------------------------------------------
771      # and finish group box
772      layout.setMargin(g_spacing)
773      layout.setSpacing(g_spacing)
774      glayout.addWidget(frame)
775      glayout.setSpacing(g_glayout_spacing)
776      gbox.setLayout(glayout)
777      return gbox
778
779   def group_box_expected(self):
780      """create a group box with a VBox layout:
781         for controlling sujbect vars: tcat_nfirst, volreg_base, blur_size
782                                       motion_limit
783      """
784
785      gbox = self.get_styled_group_box("expected options")
786
787      # put frame inside gbox, which we can hide via toggled button
788      glayout = QtGui.QVBoxLayout(gbox)
789      frame = QtGui.QFrame(gbox)
790      frame.setFrameShape(QtGui.QFrame.NoFrame)
791      gbox.frame = frame
792      self.init_gbox_viewable(gbox, True)       # default to viewable
793      # gbox.toggled.connect(self.gbox_toggle_frame)
794      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
795
796      layout = QtGui.QGridLayout(frame)         # now a child of frame
797      posn = 0
798
799      # --------------------------------------------------
800      # tcat_nfirst
801      label = QtGui.QLabel("first TRs to remove (per run)")
802      label.setStatusTip("the number of pre-steady state TRs to remove")
803      self.gvars.Line_tcat_nfirst = QtGui.QLineEdit()
804      self.gvars.Line_tcat_nfirst.setText(self.svars.tcat_nfirst)
805      self.connect(self.gvars.Line_tcat_nfirst,
806                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
807
808      layout.addWidget(label, posn, 0)
809      layout.addWidget(self.gvars.Line_tcat_nfirst, posn, 2)
810      posn += 1
811
812      # --------------------------------------------------
813      # volreg_base
814      label = QtGui.QLabel("volume registration base")
815      label.setStatusTip("EPI registration volume (after TR removal)")
816      self.gvars.Line_volreg_base = QtGui.QLineEdit()
817      # choose button
818      blist = ['vr base: %s' % base for base in USUBJ.g_vreg_base_list]
819      pbut = QLIB.create_menu_button(frame, "choose", blist,
820                call_back=self.CB_gbox_PushB)
821      self.gvars.Line_volreg_base.setText(self.svars.volreg_base)
822      self.connect(self.gvars.Line_volreg_base,
823                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
824
825      layout.addWidget(label, posn, 0)
826      layout.addWidget(pbut, posn, 1)
827      layout.addWidget(self.gvars.Line_volreg_base, posn, 2)
828      posn += 1
829
830      # --------------------------------------------------
831      # blur size
832      label = QtGui.QLabel("blur size (FWHM in mm)")
833      label.setStatusTip("Full Width at Half Max of gaussian blur to apply")
834      self.gvars.Line_blur_size = QtGui.QLineEdit()
835      self.gvars.Line_blur_size.setText(self.svars.blur_size)
836      self.connect(self.gvars.Line_blur_size,
837                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
838      layout.addWidget(label, posn, 0)
839      layout.addWidget(self.gvars.Line_blur_size, posn, 2)
840      posn += 1
841
842      # --------------------------------------------------
843      # motion_limit
844      label = QtGui.QLabel("motion censor limit (mm, per TR)")
845      label.setStatusTip("censor TRs with motion exceeding this mm distance")
846      self.gvars.Line_motion_limit = QtGui.QLineEdit()
847      self.gvars.Line_motion_limit.setText(self.svars.motion_limit)
848      self.connect(self.gvars.Line_motion_limit,
849                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
850      layout.addWidget(label, posn, 0)
851      layout.addWidget(self.gvars.Line_motion_limit, posn, 2)
852      posn += 1
853
854      frame.setLayout(layout)
855      layout.setMargin(g_spacing)
856      layout.setSpacing(g_spacing)
857      glayout.addWidget(frame)
858      glayout.setSpacing(g_glayout_spacing)
859      gbox.setLayout(glayout)
860      return gbox
861
862   def group_box_anat(self):
863      """create a group box with a VBox layout:
864                HBox:  QPushB(browse anat)  QPushB(clear anat)
865                LineEdit(anatomical dataset)
866                QCheckBox (inlude tlrc)
867
868         for controlling sujbect vars: anat, get_tlrc
869      """
870
871      gbox = self.get_styled_group_box("anatomical dataset")
872
873      # put frame inside gbox, which we can hide via toggled button
874      glayout = QtGui.QVBoxLayout(gbox)
875      frame = QtGui.QFrame(gbox)
876      frame.setFrameShape(QtGui.QFrame.NoFrame)
877      gbox.frame = frame
878      self.init_gbox_viewable(gbox, True)       # default to viewable
879      # gbox.toggled.connect(self.gbox_toggle_frame)
880      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
881
882      layout = QtGui.QVBoxLayout(frame) # now a child of frame
883
884      # create an HBox Widget with 2 buttons
885      labels = ['browse anat', 'clear anat', 'help']
886      tips = ['browse file system for anatomical dataset',
887              'clear anatomical dataset entry',
888              'display help for this section' ]
889      bwidget = QLIB.create_button_list_widget(labels, cb=self.CB_gbox_PushB,
890                                         tips=tips, hind=2, style=self.style())
891      gbox.bdict = bwidget.bdict
892      layout.addWidget(bwidget)
893
894      # create the anat file browsing dialog
895      gbox.FileD = self.make_file_dialog("load anatomical dataset", "",
896                     "*.HEAD;;*.nii;;*.nii.gz;;*")
897                     #".HEAD files (*.HEAD);;.nii files (*.nii);;all files (*)")
898
899      # add a line for the anat name, init to anat
900      self.gvars.Line_anat = QtGui.QLineEdit()
901      self.gvars.Line_anat.setText(self.svars.anat)
902      self.connect(self.gvars.Line_anat,
903                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
904      layout.addWidget(self.gvars.Line_anat)
905
906      # --------------------------------------------------
907      # checkbox: anat_has_skull
908      cbox = QtGui.QCheckBox("anat has skull")
909      cbox.setStatusTip("check if input anat has skull, clear if not")
910      cbox.setChecked(self.svars.anat_has_skull=='yes')
911      # cbox.clicked.connect(self.CB_checkbox)
912      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
913
914      layout.addWidget(cbox)
915      gbox.checkBox_anat_has_skull = cbox
916
917      # --------------------------------------------------
918      # add a checkbox for including tlrc, init with get_tlrc
919      cbox = QtGui.QCheckBox("include copy of anat+tlrc")
920      cbox.setChecked(self.svars.get_tlrc=='yes')
921      # gbox.checkBox.clicked.connect(self.CB_checkbox)
922      self.connect(cbox, QtCore.SIGNAL('clicked()'), self.CB_checkbox)
923      layout.addWidget(cbox)
924      gbox.checkBox = cbox
925
926      # --------------------------------------------------
927      layout.setMargin(g_spacing)
928      layout.setSpacing(g_spacing)
929      frame.setLayout(layout)
930      glayout.addWidget(frame)
931      glayout.setSpacing(g_glayout_spacing)
932      gbox.setLayout(glayout)
933      return gbox
934
935   def init_gbox_viewable(self, box, view):
936      box.setCheckable(True)
937      box.setChecked(view)
938      if view: box.frame.show()
939      else:    box.frame.hide()
940
941   def gbox_toggle_frame(self):
942      obj = self.sender()
943      if obj.isChecked(): obj.frame.show()
944      else: obj.frame.hide()
945
946   def gbox_clicked(self):
947      obj = self.sender()
948      if obj.isChecked(): obj.frame.show()
949      else: obj.frame.hide()
950
951   def group_box_epi(self):
952      """create a group box with a VBox layout for EPI datasets:
953                HBox:  QPushB(browse EPI)  QPushB(clear EPI)
954                QTableWidget(EPI datasets)
955                QWidget, 3x2 Grid Layout
956                    QLabel(EPI directory)   QLabel(shadow text)
957                    QLabel(wildcard form)   QLabel(shadow text)
958                    QLabel(dataset count)   QLabel(shadow text)
959                QCheckBox (use wildcard form)
960
961         for controlling sujbect vars: epi, epi_wildcard
962      """
963
964      # --------------------------------------------------
965      gbox = self.get_styled_group_box("EPI datasets")
966
967      # put frame inside gbox, which we can hide via toggled button
968      glayout = QtGui.QVBoxLayout(gbox)
969      frame = QtGui.QFrame(gbox)
970      frame.setFrameShape(QtGui.QFrame.NoFrame)
971      glayout.addWidget(frame)
972      gbox.frame = frame
973      self.init_gbox_viewable(gbox, True)       # default to viewable
974      # gbox.toggled.connect(self.gbox_toggle_frame)
975      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
976
977      mainlayout = QtGui.QVBoxLayout(frame)     # now a child of frame
978
979      # --------------------------------------------------
980      # create an HBox Widget with 2 buttons
981      labels = ['browse EPI', 'clear EPI', 'help']
982      tips = ['browse file system for EPI datasets',
983              'clear EPI dataset entries',
984              'display help for this section' ]
985      bwidget = QLIB.create_button_list_widget(labels, cb=self.CB_gbox_PushB,
986                                         tips=tips, hind=2, style=self.style())
987      gbox.bdict = bwidget.bdict
988      mainlayout.addWidget(bwidget)
989
990      # --------------------------------------------------
991      # add a table for the epi names, init to epi
992      self.make_epi_table(gbox)
993      mainlayout.addWidget(self.gvars.Table_epi)
994
995      # --------------------------------------------------
996      # add lines for the number of datasets and wildcard form
997      bwidget = QtGui.QWidget()
998      # layout = QtGui.QHBoxLayout()
999      layout = QtGui.QGridLayout()
1000
1001      nlabel, tlabel = QLIB.create_display_label_pair('EPI directory', '')
1002      layout.addWidget(nlabel, 0, 0)
1003      layout.addWidget(tlabel, 0, 1)
1004      self.gvars.Label_epi_dir = tlabel
1005
1006      nlabel, tlabel = QLIB.create_display_label_pair('wildcard form', '')
1007      layout.addWidget(nlabel, 1, 0)
1008      layout.addWidget(tlabel, 1, 1)
1009      self.gvars.Label_epi_wildcard = tlabel
1010
1011      nlabel, tlabel = QLIB.create_display_label_pair('dataset count',
1012                                        '%s' % len(self.svars.epi))
1013      layout.addWidget(nlabel, 2, 0)
1014      layout.addWidget(tlabel, 2, 1)
1015      self.gvars.Label_epi_ndsets = tlabel
1016
1017      # basically fix column 0 and let column 1 grow
1018      layout.setColumnStretch(0, 1)
1019      layout.setColumnStretch(1, 20)
1020
1021      layout.setMargin(g_spacing)
1022      layout.setSpacing(g_grid_spacing)
1023      bwidget.setLayout(layout)
1024      mainlayout.addWidget(bwidget)
1025
1026      # --------------------------------------------------
1027      # add a checkbox for use wildcard form
1028      gbox.checkBox_wildcard = QtGui.QCheckBox("use wildcard form")
1029      # gbox.checkBox_wildcard.clicked.connect(self.CB_checkbox)
1030      self.connect(gbox.checkBox_wildcard, QtCore.SIGNAL('clicked()'),
1031                   self.CB_checkbox)
1032      gbox.checkBox_wildcard.setChecked(self.svars.epi_wildcard=='yes')
1033      mainlayout.addWidget(gbox.checkBox_wildcard)
1034
1035      # --------------------------------------------------
1036      mainlayout.setMargin(g_spacing)
1037      mainlayout.setSpacing(g_spacing)
1038      frame.setLayout(mainlayout)
1039      return gbox
1040
1041   def group_box_stim(self):
1042      """create a group box with a VBox layout for stimulus timing files:
1043                HBox:  QPushB(browse stim)  QPushB(clear stim)
1044                QTableWidget(stimulus timing files)
1045                QWidget, 3x2 Grid Layout
1046                    QLabel(stim directory)  QLabel(shadow text)
1047                    QLabel(wildcard form)   QLabel(shadow text)
1048                    QLabel(dataset count)   QLabel(shadow text)
1049                QCheckBox (use wildcard form)
1050
1051         for controlling sujbect vars: stim, stim_wildcard
1052      """
1053
1054      # --------------------------------------------------
1055      gbox = self.get_styled_group_box("stimulus timing files")
1056
1057      # put frame inside gbox, which we can hide via toggled button
1058      glayout = QtGui.QVBoxLayout(gbox)
1059      frame = QtGui.QFrame(gbox)
1060      frame.setFrameShape(QtGui.QFrame.NoFrame)
1061      glayout.addWidget(frame)
1062      gbox.frame = frame
1063      self.init_gbox_viewable(gbox, True)       # default to viewable
1064      # gbox.toggled.connect(self.gbox_toggle_frame)
1065      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
1066
1067      mainlayout = QtGui.QVBoxLayout(frame)     # now a child of frame
1068
1069      # --------------------------------------------------
1070      # create an HBox Widget with 2 buttons
1071      labels = ['browse stim', 'clear stim', 'help']
1072      tips = ['browse file system for stimulus timing files',
1073              'clear stim file entries',
1074              'display help for this section' ]
1075      bwidget = QLIB.create_button_list_widget(labels, cb=self.CB_gbox_PushB,
1076                                         tips=tips, hind=2, style=self.style())
1077      gbox.bdict = bwidget.bdict
1078      mainlayout.addWidget(bwidget)
1079
1080      # --------------------------------------------------
1081      # add a table for the stim names, init to stim
1082      self.make_stim_table(gbox)
1083
1084      # put table in its own HBox
1085      bwidget = QtGui.QWidget()
1086      layout = QtGui.QHBoxLayout()
1087      layout.addWidget(self.gvars.Table_stim)
1088      layout.setMargin(g_spacing)
1089      layout.setSpacing(g_spacing)
1090      bwidget.setLayout(layout)
1091      mainlayout.addWidget(bwidget)
1092      # mainlayout.addWidget(self.gvars.Table_stim)
1093
1094      # --------------------------------------------------
1095      # add lines for the number of datasets and wildcard form
1096      bwidget = QtGui.QWidget()
1097      layout = QtGui.QGridLayout()
1098
1099      nlabel, tlabel = QLIB.create_display_label_pair('stim directory', '')
1100      layout.addWidget(nlabel, 0, 0)
1101      layout.addWidget(tlabel, 0, 1)
1102      self.gvars.Label_stim_dir = tlabel
1103
1104      nlabel, tlabel = QLIB.create_display_label_pair('wildcard form', '')
1105      layout.addWidget(nlabel, 1, 0)
1106      layout.addWidget(tlabel, 1, 1)
1107      self.gvars.Label_stim_wildcard = tlabel
1108
1109      nlabel, tlabel = QLIB.create_display_label_pair('stim file count',
1110                                            '%s' % len(self.svars.stim))
1111      layout.addWidget(nlabel, 2, 0)
1112      layout.addWidget(tlabel, 2, 1)
1113      self.gvars.Label_stim_ndsets = tlabel
1114
1115      # basically fix column 0 and let column 1 grow
1116      layout.setColumnStretch(0, 1)
1117      layout.setColumnStretch(1, 20)
1118
1119      layout.setMargin(g_spacing)
1120      layout.setSpacing(g_grid_spacing)
1121      bwidget.setLayout(layout)
1122      mainlayout.addWidget(bwidget)
1123
1124      # --------------------------------------------------
1125      # add a new line for setting basis functions:
1126      # Label  PushButton.Menu   LineEdit
1127      bwidget = QtGui.QWidget()
1128      layout = QtGui.QHBoxLayout()
1129
1130      nlabel = QtGui.QLabel("init basis funcs:")
1131      nlabel.setStatusTip("initialization for all stim files")
1132      layout.addWidget(nlabel)
1133
1134      blist = USUBJ.g_def_stim_basis_list
1135      blist = ['basis: %s'%basis for basis in blist]
1136      pbut = QLIB.create_menu_button(bwidget, "choose", blist,
1137                call_back=self.CB_gbox_PushB)
1138      layout.addWidget(pbut)
1139
1140      self.gvars.Line_apply_basis  = QtGui.QLineEdit()
1141      if len(self.svars.stim_basis) > 0: basis = self.svars.stim_basis[0]
1142      else:                              basis = ''
1143      self.gvars.Line_apply_basis.setText(basis)
1144      self.connect(self.gvars.Line_apply_basis,
1145                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
1146      layout.addWidget(self.gvars.Line_apply_basis)
1147
1148      layout.setMargin(g_spacing)
1149      layout.setSpacing(g_spacing)
1150      bwidget.setLayout(layout)
1151      mainlayout.addWidget(bwidget)
1152
1153      # --------------------------------------------------
1154      # add a new line for setting file types:
1155      # Label  PushButton.Menu   LineEdit
1156      bwidget = QtGui.QWidget()
1157      layout = QtGui.QHBoxLayout()
1158
1159      nlabel = QtGui.QLabel("init file types:")
1160      nlabel.setStatusTip("initialization for all stim files")
1161      layout.addWidget(nlabel)
1162
1163      blist = USUBJ.g_def_stim_types_list
1164      blist = ['stype: %s'%tt for tt in blist]
1165      pbut = QLIB.create_menu_button(bwidget, "choose", blist,
1166                call_back=self.CB_gbox_PushB)
1167      layout.addWidget(pbut)
1168
1169      self.gvars.Line_apply_stype = QtGui.QLineEdit()
1170      if len(self.svars.stim_type) > 0: stype = self.svars.stim_type[0]
1171      else:                             stype = ''
1172      self.gvars.Line_apply_stype.setText(stype)
1173      self.connect(self.gvars.Line_apply_stype,
1174                   QtCore.SIGNAL('editingFinished()'), self.CB_line_text)
1175      layout.addWidget(self.gvars.Line_apply_stype)
1176
1177      layout.setMargin(g_spacing)
1178      layout.setSpacing(g_spacing)
1179      bwidget.setLayout(layout)
1180      mainlayout.addWidget(bwidget)
1181
1182      # --------------------------------------------------
1183      # add a checkbox for use wildcard form
1184      gbox.checkBox_wildcard = QtGui.QCheckBox("use wildcard form")
1185      # gbox.checkBox_wildcard.clicked.connect(self.CB_checkbox)
1186      self.connect(gbox.checkBox_wildcard, QtCore.SIGNAL('clicked()'),
1187                   self.CB_checkbox)
1188      gbox.checkBox_wildcard.setChecked(self.svars.stim_wildcard=='yes')
1189      mainlayout.addWidget(gbox.checkBox_wildcard)
1190
1191      # --------------------------------------------------
1192      mainlayout.setMargin(g_spacing)
1193      mainlayout.setSpacing(g_spacing)
1194      frame.setLayout(mainlayout)
1195
1196      return gbox
1197
1198   def group_box_gltsym(self):
1199      """create a group box with a VBox layout for symbolic GLTs
1200                HBox:  QPushB(clear GLTs)  QPushB(help: gltsym)
1201                QTableWidget(label SYM:)
1202                QWidget, 3x2 Grid Layout
1203                    QLabel(dataset count)   QLabel(shadow text)
1204
1205         for controlling sujbect vars: gltsym, gltsym_label
1206      """
1207
1208      # --------------------------------------------------
1209      gbox = self.get_styled_group_box("symbolic GLTs")
1210
1211      # put frame inside gbox, which we can hide via toggled button
1212      glayout = QtGui.QVBoxLayout(gbox)
1213      frame = QtGui.QFrame(gbox)
1214      frame.setFrameShape(QtGui.QFrame.NoFrame)
1215      glayout.addWidget(frame)
1216      glayout.setSpacing(g_glayout_spacing)
1217      gbox.frame = frame
1218      self.init_gbox_viewable(gbox, False)      # default to hidden
1219      # gbox.toggled.connect(self.gbox_toggle_frame)
1220      self.connect(gbox, QtCore.SIGNAL('clicked()'), self.gbox_clicked)
1221
1222      mainlayout = QtGui.QVBoxLayout(frame)     # now a child of frame
1223
1224      # --------------------------------------------------
1225      # create an HBox Widget with 3 action buttons
1226      labels = ['insert glt row', 'init with examples']
1227      tips = ['append a blank row to the table',
1228              'initialize table with GLTs from stim labels' ]
1229      bwidget = QLIB.create_button_list_widget(labels, cb=self.CB_gbox_PushB,
1230                                               tips=tips, style=self.style())
1231      gbox.bdict = bwidget.bdict
1232      mainlayout.addWidget(bwidget)
1233
1234      # --------------------------------------------------
1235      # create an HBox Widget with 2 buttons
1236      labels = ['resize glt table', 'clear glt table', 'help']
1237      tips = ['delete any blank rows from table',
1238              'clear all entries from table',
1239              'display help for this section' ]
1240      bwidget = QLIB.create_button_list_widget(labels, cb=self.CB_gbox_PushB,
1241                                         tips=tips, hind=2, style=self.style())
1242      for key in list(bwidget.bdict.keys()):  # copy additional keys
1243         gbox.bdict[key] = bwidget.bdict[key]
1244      mainlayout.addWidget(bwidget)
1245
1246      ##### should we use a grid instead?
1247      # labels = ['insert glt row', 'init with glt examples', None,
1248      #           'resize glt table', 'clear glt table', 'help: gltsym']
1249      # tips   = ['append a blank row to the table',
1250      #           'initialize table with GLTs from stim labels', None,
1251      #           'delete any blank rows from table',
1252      #           'clear all entries from table',
1253      #           'display help for this section' ]
1254      # bwidget =QLIB.create_button_grid(labels,tips=tips,cb=self.CB_gbox_PushB)
1255      # icon = self.style().standardIcon(QtGui.QStyle.SP_MessageBoxQuestion)
1256      # gbox.blist = bwidget.blist
1257      # gbox.blist[4].setIcon(icon)
1258      # mainlayout.addWidget(bwidget)
1259
1260      # --------------------------------------------------
1261      # add a table for the stim names, init to stim
1262      self.make_gltsym_table(gbox)
1263
1264      # put table in its own HBox
1265      bwidget = QtGui.QWidget()
1266      layout = QtGui.QHBoxLayout()
1267      layout.addWidget(self.gvars.Table_gltsym)
1268      bwidget.setLayout(layout)
1269      mainlayout.addWidget(bwidget)
1270      # mainlayout.addWidget(self.gvars.Table_gltsym)
1271
1272      # --------------------------------------------------
1273      # add lines for the number of datasets and wildcard form
1274      bwidget = QtGui.QWidget()
1275      layout = QtGui.QGridLayout()
1276
1277      nlabel, tlabel = QLIB.create_display_label_pair('GLT count',
1278                                            '%s' % len(self.svars.gltsym))
1279      layout.addWidget(nlabel, 0, 0)
1280      layout.addWidget(tlabel, 0, 1)
1281      self.gvars.Label_gltsym_len = tlabel
1282
1283      # basically fix column 0 and let column 1 grow
1284      layout.setColumnStretch(0, 1)
1285      layout.setColumnStretch(1, 20)
1286
1287      layout.setMargin(0)
1288      bwidget.setLayout(layout)
1289      mainlayout.addWidget(bwidget)
1290
1291      # --------------------------------------------------
1292      mainlayout.setMargin(g_spacing)
1293      mainlayout.setSpacing(g_spacing)
1294      frame.setLayout(mainlayout)
1295
1296      return gbox
1297
1298   def make_gltsym_table(self, parent=None):
1299      """init table from gltsym and _label arrays
1300         - 3 columns: index, label, SYM:
1301      """
1302      col_heads = ["label", "symbolic GLT"]
1303      nrows = len(self.svars.gltsym)             # one row per gltsym
1304      ncols = len(col_heads)
1305
1306      table = QtGui.QTableWidget(nrows, ncols)
1307      table.stretch_cols = [1]                   # columns that should stretch
1308
1309      table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1310      table.setHorizontalHeaderLabels(col_heads)
1311
1312      self.resize_table_cols(table)
1313
1314      # set and fill the table
1315      self.gvars.Table_gltsym = table
1316      self.gltsym_list_to_table()
1317
1318   def gltsym_list_to_table(self, symlist=None, symlabs=None):
1319      """update Table_gltsym from gltsym and _label arrays
1320      """
1321      table = self.gvars.Table_gltsym           # convenience
1322      if symlist == None: sym  = self.svars.gltsym
1323      else:               sym  = symlist
1324      if symlabs == None: labs = self.svars.gltsym_label
1325      else:               labs = symlabs
1326
1327      nrows = len(sym)
1328
1329      table.setRowCount(0)                      # init, add rows per file
1330      table.setSortingEnabled(False)            # sort only after filling table
1331
1332      if nrows <= 0:    # clear extra fields (if created) and return
1333         if self.gvars.valid('Label_gltsym_len'):
1334            self.gvars.Label_gltsym_len.setText('0')
1335            self.resize_table(table)
1336         return
1337
1338      # if we don't have the correct number of labels, start from scratch
1339      if len(labs) != nrows:
1340         labs = ['' for glt in sym]
1341
1342      if self.verb > 2: print("== gltsym table, nGLT = %d" % (nrows))
1343
1344      # ------------------------------------------------------------
1345      # now fill table with label and GLTs
1346
1347      for ind, glt in enumerate(sym):
1348         labItem = QtGui.QTableWidgetItem(labs[ind])
1349         nameItem = QtGui.QTableWidgetItem(glt)
1350         table.insertRow(ind)           # insert at end
1351         table.setItem(ind, 0, labItem)
1352         table.setItem(ind, 1, nameItem)
1353
1354      table.resizeRowsToContents()
1355      table.setAlternatingRowColors(True)
1356
1357      # set min size base on ~25 per row, with min of 75 and max of 200
1358      # table.setMinimumSize(100, self.max_table_size(nrows))
1359      table.setMinimumSize(100, 75)
1360      self.resize_table(table)
1361
1362      # no sorting here
1363      table.setSortingEnabled(False)
1364
1365      # ------------------------------------------------------------
1366      # and fill in Label_stim_ndsets, stim_dir, and stim_wildcard (form)
1367      self.gvars.Label_gltsym_len.setText('%d' % nrows)
1368
1369   def make_file_dialog(self, title, dirname, filtname):
1370      fileD = QtGui.QFileDialog(self)
1371      # QString no longer exists in python3
1372      try:
1373         tstr = QtCore.QString(title)
1374         fstr = QtCore.QString(filtname)
1375         dstr = QtCore.QString(dirname)
1376      except:
1377         tstr = title
1378         fstr = filtname
1379         dstr = dirname
1380      fileD.setWindowTitle(tstr)
1381      fileD.setFilter(fstr)
1382      fileD.setDirectory(dstr)
1383      return fileD
1384
1385   def make_epi_table(self, parent=None):
1386      """init table from epi array
1387         - only update epi array on directory scan and 'update AP command'
1388      """
1389      col_heads = ['scan index', 'EPI dataset']  # column headings
1390      nrows = len(self.svars.epi)                # one row per EPI dataset
1391      ncols = len(col_heads)
1392
1393      table = QtGui.QTableWidget(nrows, ncols)
1394      table.stretch_cols = [1]                  # columns that should stretch
1395
1396      table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1397      table.setHorizontalHeaderLabels(col_heads)
1398
1399      self.resize_table_cols(table)
1400
1401      self.gvars.Table_epi = table
1402      self.epi_list_to_table()
1403
1404   def epi_list_to_table(self, epilist=None):
1405      """update Table_epi from epi array"""
1406      table = self.gvars.Table_epi              # convenience
1407      if epilist == None: epi = self.svars.epi
1408      else:               epi = epilist
1409      nrows = len(epi)
1410
1411      table.setRowCount(0)                      # init, add rows per file
1412      table.setSortingEnabled(False)            # sort only after filling table
1413
1414      if nrows <= 0:    # clear extra fields (if created) and return
1415         if self.gvars.valid('Label_epi_ndsets'):
1416            self.gvars.Label_epi_ndsets.setText('0')
1417            self.gvars.Label_epi_dir.setText('')
1418            self.gvars.Label_epi_wildcard.setText('')
1419            self.resize_table(table)
1420         return
1421
1422      # parse the EPI list into directory, short names, glob string
1423      epi_dir, short_names, globstr = UTIL.flist_to_table_pieces(epi)
1424
1425      # ------------------------------------------------------------
1426      # note wildcard form and try to create index list
1427      indlist = UTIL.list_minus_glob_form(short_names)
1428
1429      # indlist list is either list of string integers or empty strings
1430      haveinds = 0
1431      if len(indlist) < nrows: indlist = ['' for ind in range(nrows)]
1432      else:
1433         try:
1434            indlist = [int(val) for val in indlist]
1435            haveinds = 1
1436         except: indlist = ['' for ind in range(nrows)]
1437
1438      # ------------------------------------------------------------
1439      # get max index for zero padding
1440      digits = 0
1441      if haveinds:
1442         maxind = UTIL.maxabs(indlist)
1443         digits = UTIL.ndigits_lod(maxind)
1444
1445      if self.verb > 2:
1446         print("== epi table, ndsets = %d, dir = %s" % (nrows, epi_dir))
1447         print("   wildcard string = %s" % globstr)
1448         print("   indlist  = %s" % indlist)
1449
1450      # ------------------------------------------------------------
1451      # now fill table with indlist and epi (short_names)
1452
1453      for ind, dset in enumerate(short_names):
1454         nameItem = QtGui.QTableWidgetItem(dset)
1455         if haveinds:
1456            indItem = QtGui.QTableWidgetItem('%0*d' % (digits, indlist[ind]))
1457         else: indItem = QtGui.QTableWidgetItem('')
1458         indItem.setTextAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
1459         table.insertRow(ind)           # insert at end
1460         table.setItem(ind, 0, indItem)
1461         table.setItem(ind, 1, nameItem)
1462
1463      table.resizeRowsToContents()
1464      table.setAlternatingRowColors(True)
1465      # table.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
1466
1467      # set min size base on ~25 per row, with min of 200 and max of 325
1468      # table.setMinimumSize(100, self.max_table_size(nrows))
1469      table.setMinimumSize(100, 75)
1470      self.resize_table(table)
1471
1472      # if we have a scan index, default to using it for sorting
1473      table.setSortingEnabled(True)
1474      if haveinds: table.sortItems(0)
1475      else:        table.sortItems(1)
1476
1477      # ------------------------------------------------------------
1478      # and fill in Label_epi_ndsets, epi_dir, and epi_wildcard (form)
1479      self.gvars.Label_epi_ndsets.setText('%d' % nrows)
1480      self.gvars.Label_epi_dir.setText(epi_dir)
1481      self.gvars.Label_epi_wildcard.setText(globstr)
1482
1483   def make_stim_table(self, parent=None):
1484      """init table from stim array
1485         - only update stim array on directory scan and 'update AP command'
1486         - 3 columns: index, label, filename
1487      """
1488      col_heads = ['index', 'label', 'basis', 'type', 'stim (timing) file']
1489      nrows = len(self.svars.stim)              # one row per stim file
1490      ncols = len(col_heads)
1491
1492      table = QtGui.QTableWidget(nrows, ncols)
1493      table.stretch_cols = [4]                  # columns that should stretch
1494
1495      table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
1496      table.setHorizontalHeaderLabels(col_heads)
1497
1498      self.resize_table_cols(table)
1499
1500      # set and fill the table
1501      self.gvars.Table_stim = table
1502      self.stim_list_to_table()
1503
1504   def resize_table_cols(self, table):
1505      """resize to column contents, unless it is in strech_cols"""
1506
1507      ncols = table.columnCount()
1508
1509      # resize columns to fit data
1510      for col in range(ncols):          # to be safe, work on column indices
1511         if col in table.stretch_cols:
1512           table.horizontalHeader().setResizeMode(col,QtGui.QHeaderView.Stretch)
1513         else:
1514           table.horizontalHeader().setResizeMode(col,
1515                                    QtGui.QHeaderView.ResizeToContents)
1516
1517   def stim_list_to_table(self, stimlist=None, make_labs=0):
1518      """update Table_stim from stim array
1519
1520         Try to parse filenames into the form PREFIX.INDEX.LABEL.SUFFIX,
1521         where the separators can be '.' or '_'.
1522      """
1523      table = self.gvars.Table_stim              # convenience
1524      if stimlist == None: stim = self.svars.stim
1525      else:                stim = stimlist
1526      nrows = len(stim)
1527
1528      table.setRowCount(0)                      # init, add rows per file
1529      table.setSortingEnabled(False)            # sort only after filling table
1530
1531      if nrows <= 0:    # clear extra fields (if created) and return
1532         if self.gvars.valid('Label_stim_ndsets'):
1533            self.gvars.Label_stim_ndsets.setText('0')
1534            self.gvars.Label_stim_dir.setText('')
1535            self.gvars.Label_stim_wildcard.setText('')
1536            self.resize_table(table)
1537         return
1538
1539      # parse the stim list into directory, short names, glob string
1540      stim_dir, short_names, globstr = UTIL.flist_to_table_pieces(stim)
1541
1542      # ------------------------------------------------------------
1543
1544      # get index and label lists
1545      stim_table = UTIL.parse_as_stim_list(short_names)
1546      indlist = [entry[0] for entry in stim_table]
1547
1548      # if we don't have the correct number of labels, override make_labs
1549      if len(self.svars.stim_label) != nrows: make_labs = 1
1550      if make_labs: lablist = [entry[1] for entry in stim_table]
1551      else: lablist = self.svars.stim_label
1552
1553      if self.verb > 2:
1554         print("== stim table, ndsets = %d, dir = %s" % (nrows, stim_dir))
1555         print("   wildcard string = %s" % globstr)
1556         print("   stim_table: %s" % stim_table)
1557
1558      haveinds = 1
1559      for ind, val in enumerate(indlist):
1560         if val < 0:
1561            if self.verb > 1:
1562               print('-- bad stim_table index %d = %d, skipping indices' \
1563                     % (ind, val))
1564            haveinds = 0
1565            break
1566
1567      # get max index for zero padding
1568      digits = 0
1569      if haveinds:
1570         maxind = UTIL.maxabs(indlist)
1571         digits = UTIL.ndigits_lod(maxind)
1572
1573      # ------------------------------------------------------------
1574      # make basis function list
1575      nbases = len(self.svars.stim_basis)
1576      if nbases == 0:
1577         bases = ['GAM' for i in range(nrows)]
1578      elif nbases == 1:
1579         bases = [self.svars.stim_basis[0] for i in range(nrows)]
1580      elif nbases != nrows:
1581         tt = '** len(stim_basis) == %d, but have %d stim\n\n' \
1582              '   clearning list, please fill...' % (nbases, nrows)
1583         update_AP_warn_window(tt)
1584         bases = ['' for i in range(nrows)]
1585      else:
1586         bases = self.svars.stim_basis
1587
1588      # ------------------------------------------------------------
1589      # make stim_type list
1590      ntypes = len(self.svars.stim_type)
1591      if ntypes == 0:
1592         stypes = ['times' for i in range(nrows)]
1593      elif ntypes == 1:
1594         stypes = [self.svars.stim_type[0] for i in range(nrows)]
1595      elif ntypes != nrows:
1596         tt = '** len(stim_type) == %d, but have %d stim\n\n' \
1597              '   clearning list, please fill...' % (ntypes, nrows)
1598         update_AP_warn_window(tt)
1599         stypes = ['' for i in range(nrows)]
1600      else:
1601         stypes = self.svars.stim_type
1602
1603      # ------------------------------------------------------------
1604      # now fill table with index, label and filename (short_names)
1605
1606      for ind, dset in enumerate(short_names):
1607         if haveinds:
1608            indItem = QtGui.QTableWidgetItem('%0*d'%(digits, indlist[ind]))
1609         else:
1610            indItem = QtGui.QTableWidgetItem('')
1611         indItem.setTextAlignment(QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter)
1612
1613         labItem = QtGui.QTableWidgetItem(lablist[ind])
1614         basisItem = QtGui.QTableWidgetItem(bases[ind])
1615         typeItem = QtGui.QTableWidgetItem(stypes[ind])
1616         nameItem = QtGui.QTableWidgetItem(dset)
1617         table.insertRow(ind)           # insert at end
1618         table.setItem(ind, 0, indItem)
1619         table.setItem(ind, 1, labItem)
1620         table.setItem(ind, 2, basisItem)
1621         table.setItem(ind, 3, typeItem)
1622         table.setItem(ind, 4, nameItem)
1623
1624      table.resizeRowsToContents()
1625      table.setAlternatingRowColors(True)
1626      # table.setDragDropMode(QtGui.QAbstractItemView.InternalMove)
1627      # table.setDragEnabled(True)
1628
1629      # set min size base on ~25 per row, with min of 75 and max of 200
1630      # table.setMinimumSize(100, self.max_table_size(nrows))
1631      table.setMinimumSize(100, 75)
1632      self.resize_table(table)
1633
1634      # if we have a stim index, default to using it for sorting
1635      table.setSortingEnabled(True)
1636      if haveinds: table.sortItems(0)
1637      else:        table.sortItems(4)
1638
1639      # ------------------------------------------------------------
1640      # and fill in Label_stim_ndsets, stim_dir, and stim_wildcard (form)
1641      self.gvars.Label_stim_ndsets.setText('%d' % nrows)
1642      self.gvars.Label_stim_dir.setText(stim_dir)
1643      self.gvars.Label_stim_wildcard.setText(globstr)
1644
1645   def max_table_size(self, nrows, rheight=0):
1646      """return the number of pixels to use based on the number of rows
1647         (scale by any passed row height)"""
1648      if rheight > 0: rowheight = rheight
1649      else:           rowheight = 25
1650
1651      if nrows <= 6:   show_rows = nrows
1652      elif nrows < 18: show_rows = 6+0.5*(nrows-6)  # after 6, add half, per
1653      else:            show_rows = 12
1654
1655      return min(200, max(75, rheight*(show_rows+1.25)))
1656
1657   def CB_checkbox(self):
1658      """call-back for any check boxes"""
1659      obj = self.sender()
1660      if   obj == self.gvars.gbox_anat.checkBox:
1661         if obj.isChecked(): self.set_svar('get_tlrc', 'yes')
1662         else:               self.set_svar('get_tlrc', 'no')
1663      elif obj == self.gvars.gbox_anat.checkBox_anat_has_skull:
1664         if obj.isChecked(): self.set_svar('anat_has_skull', 'yes')
1665         else:               self.set_svar('anat_has_skull', 'no')
1666      elif obj == self.gvars.gbox_epi.checkBox_wildcard:
1667         if obj.isChecked(): self.set_svar('epi_wildcard', 'yes')
1668         else:               self.set_svar('epi_wildcard', 'no')
1669      elif obj == self.gvars.gbox_stim.checkBox_wildcard:
1670         if obj.isChecked(): self.set_svar('stim_wildcard', 'yes')
1671         else:               self.set_svar('stim_wildcard', 'no')
1672      elif obj == self.gvars.gbox_regress.checkBox_mot_deriv:
1673         if obj.isChecked(): self.set_svar('regress_mot_deriv', 'yes')
1674         else:               self.set_svar('regress_mot_deriv', 'no')
1675      elif obj == self.gvars.gbox_regress.checkBox_reml_exec:
1676         if obj.isChecked(): self.set_svar('reml_exec', 'yes')
1677         else:               self.set_svar('reml_exec', 'no')
1678      elif obj == self.gvars.gbox_regress.checkBox_run_clustsim:
1679         if obj.isChecked(): self.set_svar('run_clustsim', 'yes')
1680         else:               self.set_svar('run_clustsim', 'no')
1681      elif obj == self.gvars.gbox_regress.checkBox_compute_fitts:
1682         if obj.isChecked(): self.set_svar('compute_fitts', 'yes')
1683         else:               self.set_svar('compute_fitts', 'no')
1684      elif obj == self.gvars.gbox_align.checkBox_align_giant_move:
1685         if obj.isChecked(): self.set_svar('align_giant_move', 'yes')
1686         else:               self.set_svar('align_giant_move', 'no')
1687      elif obj == self.gvars.gbox_tlrc.checkBox_tlrc_ok_maxite:
1688         if obj.isChecked(): self.set_svar('tlrc_ok_maxite', 'yes')
1689         else:               self.set_svar('tlrc_ok_maxite', 'no')
1690      else: print("** CB_checkbox: unknown sender")
1691
1692   def update_textLine_check(self, obj, text, attr, button_name, check_func):
1693      """check text against check_func(text, bname, 1, self, 1)
1694         - this warns the user on error
1695         - if valid, update the attr and textLine with the text
1696         - else, clear object and set focus to it (and return)
1697
1698         return 1/0 to specify whether value was applied
1699      """
1700
1701      # be sure we have a type to compare against for setting the text
1702      global g_LineEdittype
1703      if g_LineEdittype == None: g_LineEdittype = type(QtGui.QLineEdit())
1704
1705      rtext = str(text) # in case of QString
1706
1707      if check_func(rtext, button_name, 1, self, 1):
1708         self.set_svar(attr, rtext)
1709         if type(obj) == g_LineEdittype: obj.setText(text)
1710         else: print('** update_textLine_check: not a LineEdit type')
1711         return 1
1712      else:
1713         # error, reset to previous attribute
1714         # obj.clear()
1715         obj.setText(self.svars.val(attr))
1716         obj.setFocus()
1717         return 0
1718
1719   def cb_command_window(self, cmd):
1720      print('++ python exec command: %s' % cmd)
1721      exec(cmd)
1722
1723   def cb_show_py_command_window(self):
1724      """show the python command window"""
1725      if not self.gvars.valid('PCW'):
1726         self.gvars.PCW = QLIB.PyCommandWindow(callback=self.cb_command_window)
1727      self.gvars.PCW.show()
1728      self.gvars.PCW.raise_()
1729
1730   def cb_show_command_window(self):
1731      """show the shell command window"""
1732      # start a new window?
1733      if not self.gvars.valid('CW'):
1734         self.gvars.CW = QLIB.ProcessWindow(parent=self)
1735      self.gvars.CW.show()
1736      self.gvars.CW.raise_()
1737
1738   def apply_PushB_action(self, sender, vname):
1739      """if sender is an action in PushB_VNAME.act_dict, apply
1740            svars.vname = applied key
1741            set new text in button
1742
1743         return 1 if applied, -1 if canceled, and 0 otherwise
1744      """
1745      pbobj = self.gvars.val('PushB_%s' % vname)
1746      if pbobj == None: return 0
1747
1748      for key in list(pbobj.act_dict.keys()):
1749         act = pbobj.act_dict[key]
1750         if sender == act:      # found!
1751            if key == 'CANCEL': return -1
1752            self.svars.set_var(vname, key)
1753            pbobj.setText(key)
1754            return 1
1755
1756      return 0
1757
1758   def CB_gbox_PushB(self):
1759      """these buttons are associated with anat/EPI/stim file group boxes
1760         - the sender (button) text must be unique"""
1761
1762      try:
1763         sender = self.sender()
1764         text = str(sender.text())
1765      except:
1766         print('** CB_gbox_PushB: no text')
1767         return
1768
1769      # analysis init: help and APPLY
1770      if self.apply_PushB_action(sender, 'anal_type'): return
1771      elif self.apply_PushB_action(sender, 'anal_domain'): return
1772
1773      elif sender == self.gvars.PushB_anal_help:
1774
1775         cstr = USUBJ.g_rdef_strs.changed_attrs_str(USUBJ.g_sdef_strs,
1776                           skiplist='name', showskip=0, showdel=0)
1777         hstr  = "%s\n\nAnalysis type 'rest': %s" % (g_help_init, cstr)
1778         self.update_help_window(hstr, title='analysis initialization')
1779
1780      elif sender == self.gvars.PushB_anal_apply:
1781         self.init_analysis_defaults() # apply anal_type and anal_domain
1782
1783      # anat
1784      elif sender == self.gvars.gbox_anat.bdict['help']:
1785         self.update_help_window(g_help_anat, title='anatomical datasets')
1786
1787      elif text == 'browse anat':
1788         fname = QtGui.QFileDialog.getOpenFileName(self,
1789                   "load anatomical dataset", self.pick_base_dir('anat'),
1790                   "datasets (*.HEAD *.nii *.nii.gz);;all files (*)")
1791         self.update_textLine_check(self.gvars.Line_anat,
1792                fname, 'anat', 'anatomical dset', QLIB.valid_as_filepath)
1793
1794      elif text == 'clear anat':
1795         self.gvars.Line_anat.clear()
1796         self.set_svar('anat','')
1797
1798      # EPI
1799      # elif text == 'help: EPI':
1800      elif sender == self.gvars.gbox_epi.bdict['help']:
1801         self.update_help_window(g_help_epi, title='EPI datasets')
1802
1803      elif text == 'browse EPI':
1804         fnames = QtGui.QFileDialog.getOpenFileNames(self,
1805                        "load EPI datasets", self.pick_base_dir('epi'),
1806                        "datasets (*.HEAD *.nii *.nii.gz);;all files (*)")
1807         if len(fnames) > 0:
1808            self.set_svar('epi', [str(name) for name in fnames])
1809            self.epi_list_to_table()
1810
1811      elif text == 'clear EPI':
1812            self.set_svar('epi', [])
1813            self.epi_list_to_table()
1814
1815      # stim
1816      elif sender == self.gvars.gbox_stim.bdict['help']:
1817         self.update_help_window(g_help_stim, title='stim times files')
1818
1819      elif text == 'browse stim':
1820         fnames = QtGui.QFileDialog.getOpenFileNames(self,
1821                     "load stimulus timing files", self.pick_base_dir('stim'),
1822                     "files (*.txt *.1D);;all files (*)")
1823         if len(fnames) > 0:
1824            # if reading from disk, clear existing variables
1825            self.set_svar('stim', [str(name) for name in fnames])
1826            self.set_svar('stim_label', [])
1827            self.set_svar('stim_basis', [])
1828            self.set_svar('stim_type', [])
1829            self.stim_list_to_table(make_labs=1)
1830
1831      elif text == 'clear stim':
1832         self.set_svar('stim', [])
1833         self.stim_list_to_table()
1834
1835      elif text[0:7] == 'basis: ':
1836         self.update_basis_function(text[7:])
1837
1838      elif text[0:7] == 'stype: ':
1839         self.update_stim_type(text[7:])
1840
1841      # expected
1842      elif text[0:9] == 'vr base: ':
1843         base = text[9:]
1844         self.set_svar('volreg_base', base)
1845         self.gvars.Line_volreg_base.setText(base)
1846
1847      # GLT table buttons
1848
1849      elif text == 'insert glt row':
1850         table = self.gvars.Table_gltsym
1851         nrows = table.rowCount()
1852         table.insertRow(nrows)
1853         self.resize_table(table, self.gvars.Label_gltsym_len)
1854
1855      elif text == 'init with examples':
1856
1857         # get labels to apply
1858         if self.update_stim_from_table(): return
1859         if len(self.svars.stim_label) < 2:
1860            QLIB.guiWarning('Sorry', '** need at least 2 stim labels for GLTs',
1861                            self)
1862            return
1863         gltsym, gltlabs = USUBJ.make_gltsym_examples(self.svars.stim_label)
1864         self.gltsym_list_to_table(gltsym, gltlabs)
1865         self.resize_table(self.gvars.Table_gltsym, self.gvars.Label_gltsym_len)
1866
1867      # elif text == 'help: gltsym':
1868      elif sender == self.gvars.gbox_gltsym.bdict['help']:
1869         self.update_help_window(g_help_gltsym, title='symbolic GLTs')
1870
1871      elif text == 'resize glt table':
1872         """delete any rows without text"""
1873         self.remove_blank_table_rows(self.gvars.Table_gltsym,
1874                                      self.gvars.Label_gltsym_len)
1875
1876      elif text == 'clear glt table':
1877         self.set_svar('gltsym', [])
1878         self.set_svar('gltsym_label', [])
1879         self.gltsym_list_to_table()
1880         self.resize_table(self.gvars.Table_gltsym, self.gvars.Label_gltsym_len)
1881
1882      elif text[0:6] == 'cost: ':
1883         self.update_align_cost(text[6:])
1884
1885      elif text[0:10] == 'template: ':
1886         self.update_tlrc_base(text[10:])
1887
1888      else: print("** unexpected button text: %s" % text)
1889
1890   def resize_table(self, table, countLabel=None):
1891      nrows = table.rowCount()
1892      if nrows > 0: rheight = table.rowHeight(0)
1893      else        : rheight = 0
1894      if self.verb > 2: print('-- resize_table: using row height %d' % rheight)
1895      table.setAlternatingRowColors(True)
1896      table.setFixedHeight(self.max_table_size(nrows, rheight=rheight))
1897      table.resizeRowsToContents()
1898      self.resize_table_cols(table)
1899
1900      if countLabel != None: countLabel.setText('%d' % nrows)
1901
1902   def remove_blank_table_rows(self, table, countLabel=None):
1903
1904      # --------------------------------------------------
1905      # gltsym table (get labels and GLTs)
1906      nrows = table.rowCount()
1907      ncols = table.columnCount()
1908
1909      # work from row = nrows-1 down to 0, so deletion does not affect indices
1910      for row in range(nrows-1, -1, -1):
1911         found = 0
1912         # search for something in this row
1913         for col in range(ncols):
1914            item = table.item(row, col)
1915            if item != None:
1916               if str(item.text()) != '':
1917                  found = 1
1918                  break
1919         if not found: table.removeRow(row)
1920
1921      self.resize_table(table, countLabel)
1922
1923      return 0
1924
1925   def pick_base_dir(self, dtype):
1926      """return something useful or an empty string"""
1927      anat = self.svars.anat    # for ease of typing
1928      epi  = self.svars.epi
1929      stim = self.svars.stim
1930      if dtype == 'top':     # common dir to all input files
1931         all_files = []
1932         if anat != '': all_files.append(anat)
1933         all_files.extend(epi)
1934         all_files.extend(stim)
1935         return UTIL.common_dir(all_files)
1936      elif dtype == 'anat':
1937         if anat != '':      return os.path.dirname(anat)
1938         elif len(epi) > 0:  return os.path.dirname(epi[0])
1939         elif len(stim) > 0: return os.path.dirname(stim[0])
1940      elif dtype == 'epi':
1941         if len(epi) > 0:    return os.path.dirname(epi[0])
1942         elif anat != '':    return os.path.dirname(anat)
1943         elif len(stim) > 0: return os.path.dirname(stim[0])
1944      elif dtype == 'stim':
1945         if len(stim) > 0:   return os.path.dirname(stim[0])
1946         elif len(epi) > 0:  return os.path.dirname(epi[0])
1947         elif anat != '':    return os.path.dirname(anat)
1948      else:
1949         print('** pick_base_dir: bad dtype = %s' % dtype)
1950
1951      return ''
1952
1953   def update_align_cost(self, cost):
1954      if len(cost) == 0: return
1955      if self.verb > 1: print('++ applying cost function %s' % cost)
1956      self.gvars.Line_align_cost.setText(cost)
1957
1958      if self.svars.val('align_cost') != cost:  # update on change
1959         self.set_svar('align_cost', cost)
1960
1961   def update_tlrc_base(self, base):
1962      if len(base) == 0: return
1963      if self.verb > 1: print('++ applying tlrc_base %s' % base)
1964      self.gvars.Line_tlrc_base.setText(base)
1965
1966      if self.svars.val('tlrc_base') != base:  # update on change
1967         self.set_svar('tlrc_base', base)
1968
1969   def update_basis_function(self, basis):
1970      if len(basis) > 0 and not self.basis_func_is_current(basis):
1971         self.update_stim_from_table() # apply any updates to variables
1972         if self.verb > 1: print('++ applying basis function %s' % basis)
1973         self.gvars.Line_apply_basis.setText(basis)
1974         nstim = len(self.svars.stim)
1975         self.set_svar('stim_basis',[basis for i in range(nstim)])
1976         self.stim_list_to_table()
1977
1978   def basis_func_is_current(self, basis):
1979      """check a few things:
1980           - stim_basis must have length 1 or len(stim)
1981           - each entry must match 'basis'"""
1982      nstim = len(self.svars.stim)
1983      nbasis = len(self.svars.stim_basis)
1984
1985      if nbasis == 0:   # empty is special, since we cannot access entries
1986         if nstim == 0: return 1
1987         else:          return 0
1988
1989      # next check for matching lengths (or unit)
1990      if nbasis > 1 and nbasis != nstim: return 0
1991
1992      # finally, check the entries
1993      for sbasis in self.svars.stim_basis:
1994         if basis != sbasis: return 0
1995
1996      # they seem to match
1997      return 1
1998
1999   def update_stim_type(self, stype):
2000      if len(stype) > 0 and not self.stim_type_is_current(stype):
2001         self.update_stim_from_table() # apply any updates to variables
2002         if self.verb > 1: print('++ applying stim_type %s' % stype)
2003         self.gvars.Line_apply_stype.setText(stype)
2004         nstim = len(self.svars.stim)
2005         self.set_svar('stim_type',[stype for i in range(nstim)])
2006         self.stim_list_to_table()
2007
2008   def stim_type_is_current(self, stype):
2009      """check a few things:
2010           - stim_type must have length 1 or len(stim)
2011           - each entry must match 'stype'"""
2012      nstim = len(self.svars.stim)
2013      ntypes = len(self.svars.stim_type)
2014
2015      if ntypes == 0:   # empty is special, since we cannot access entries
2016         if nstim == 0: return 1
2017         else:          return 0
2018
2019      # next check for matching lengths (or unit)
2020      if ntypes > 1 and ntypes != nstim: return 0
2021
2022      # finally, check the entries
2023      for stim_type in self.svars.stim_type:
2024         if stim_type != stype: return 0
2025
2026      # they seem to match
2027      return 1
2028
2029   def add_menu_bar(self):
2030
2031      # ----------------------------------------------------------------------
2032      # main process actions
2033
2034      act1 = self.createAction("gen: afni_proc.py command",
2035        slot=self.cb_show_ap_command,
2036        tip="generate afni_proc.py command",
2037        icon=self.style().standardIcon(QtGui.QStyle.SP_FileDialogDetailedView))
2038      act2 = self.createAction("view: processing script",
2039        slot=self.cb_exec_ap_command,
2040        tip="show proc script: display output from afni_proc.py",
2041        icon=self.style().standardIcon(QtGui.QStyle.SP_FileDialogContentsView))
2042      act3 = self.createAction("process this subject",
2043          slot=self.cb_exec_proc_script,
2044          tip="process this subject: execute proc script",
2045          icon=self.style().standardIcon(QtGui.QStyle.SP_DialogYesButton))
2046
2047      act4 = self.createAction("reset all options",
2048          slot=self.cb_clear_options,
2049          tip="keep datasets: restore all other GUI options to defaults",
2050          icon=self.style().standardIcon(QtGui.QStyle.SP_TrashIcon))
2051      act5 = self.createAction("reset all fields",
2052          slot=self.cb_clear_fields,
2053          tip="restore all GUI fields to defaults",
2054          icon=self.style().standardIcon(QtGui.QStyle.SP_TrashIcon))
2055
2056      self.gvars.act_show_ap    = act1
2057      self.gvars.act_exec_ap    = act2
2058      self.gvars.act_exec_proc  = act3
2059      self.gvars.act_clear_opts = act4
2060      self.gvars.act_clear_all  = act5
2061
2062      self.gvars.act_exec_ap.setEnabled(False)
2063      self.gvars.act_exec_proc.setEnabled(False)
2064
2065      # ----------------------------------------------------------------------
2066      # File menu
2067      self.gvars.MBar_file = self.menuBar().addMenu("&File")
2068      actFileQuit = self.createAction("&Quit", slot=self.close,
2069          shortcut="Ctrl+q", tip="close the application")
2070      self.addActions(self.gvars.MBar_file, [act1, act2, act3, None,
2071                                             act4, act5, None, actFileQuit])
2072
2073      # ----------------------------------------------------------------------
2074      # View menu - all for static view windows
2075      self.gvars.MBar_view = self.menuBar().addMenu("&View")
2076
2077      act0 = self.createAction("updated variables",
2078        slot=self.cb_view,
2079        tip="display changes from defaults variables")
2080
2081      act1 = self.createAction("afni_proc.py command",
2082        slot=self.cb_view,
2083        tip="display current afni_proc.py command")
2084
2085      act2 = self.createAction("resulting proc script",
2086        slot=self.cb_view,
2087        tip="display script output by afni_proc.py")
2088
2089      act3 = self.createAction("output from proc script",
2090        slot=self.cb_view,
2091        tip="display text output from execution of proc script")
2092
2093      act4 = self.createAction("view: uber_subject.py command",
2094          slot=self.cb_view,
2095          tip="show command to populate this interface")
2096
2097      self.addActions(self.gvars.MBar_view, [act0, None,
2098                                             act1, act2, act3, None, act4])
2099
2100      self.gvars.act_view_vars     = act0
2101      self.gvars.act_view_ap_cmd   = act1
2102      self.gvars.act_view_proc     = act2
2103      self.gvars.act_view_outproc  = act3
2104      self.gvars.act_view_uber_cmd = act4
2105
2106      # ----------------------------------------------------------------------
2107      # Hidden menu
2108      self.gvars.MBar_hidden = self.menuBar().addMenu("Hidde&n")
2109
2110      # create command window, and set call back for it
2111      act1 = self.createAction("shell command window",
2112          slot=self.cb_show_command_window,
2113          tip="open command window for shell commands")
2114
2115      act2 = self.createAction("python command window",
2116          slot=self.cb_show_py_command_window,
2117          tip="open command window for local python commands")
2118
2119      act3 = self.createAction("view: subject vars",
2120        slot=self.cb_view,
2121        tip="display current subject option variables")
2122
2123      act4 = self.createAction("view: control vars",
2124        slot=self.cb_view,
2125        tip="display control option variables")
2126
2127      act5 = self.createAction("view: result vars",
2128        slot=self.cb_view,
2129        tip="display variables resulting from actions")
2130
2131      act6 = self.createAction("show: Icon keys",
2132          slot=QLIB.print_icon_names,
2133          tip="display standard Icon names")
2134
2135      act7 = self.createAction("view: GUI vars",
2136        slot=self.cb_view,
2137        tip="display GUI variables")
2138
2139      self.gvars.act_view_svars   = act3
2140      self.gvars.act_view_cvars   = act4
2141      self.gvars.act_view_rvars   = act5
2142      self.gvars.act_view_gvars   = act7
2143
2144      self.addActions(self.gvars.MBar_hidden, [act3, act4, act5, None])
2145
2146      # add show sub-menu
2147      self.gvars.Menu_commands = self.gvars.MBar_hidden.addMenu("&Commands")
2148      self.addActions(self.gvars.Menu_commands, [act1, act2])
2149
2150      # create Style sub-menu
2151      self.gvars.Menu_format = self.gvars.MBar_hidden.addMenu("set Style")
2152      actlist = []
2153      for style in g_styles:
2154         tip = 'set GUI style to %s...' % style
2155         act = self.createAction(style, slot=self.cb_setStyle, tip=tip)
2156         actlist.append(act)
2157      actlist.append(self.createAction("Default", slot=self.cb_setStyle,
2158                                tip="revert Sylte to default 'cleanlooks'"))
2159      self.addActions(self.gvars.Menu_format, actlist)
2160
2161      self.addActions(self.gvars.MBar_hidden, [None, act6, act7])
2162
2163      # ----------------------------------------------------------------------
2164      # Help menu
2165      self.gvars.MBar_help = self.menuBar().addMenu("&Help")
2166      actHelpUS = self.createAction("'uber_subject.py -help'",
2167          slot=self.cb_help_main, shortcut=QtGui.QKeySequence.HelpContents,
2168          tip="help for uber_subject.py")
2169      self.addActions(self.gvars.MBar_help, [actHelpUS])
2170
2171      # add browse actions to browse sub-menu
2172      self.gvars.Menu_browse = self.gvars.MBar_help.addMenu("&Browse")
2173      act1 = self.createAction("web: all AFNI programs",
2174          slot=self.cb_help_browse, tip="browse AFNI program help")
2175      act2 = self.createAction("web: afni_proc.py help",
2176          slot=self.cb_help_browse, tip="browse afni_proc.py help")
2177      act3 = self.createAction("web: current class handouts",
2178          slot=self.cb_help_browse, tip="browse current AFNI bootcamp handouts")
2179      act4 = self.createAction("web: tutorial-single subject analysis",
2180          slot=self.cb_help_browse, tip="browse AFNI_data6 tutorial")
2181      act5 = self.createAction("web: AFNI Message Board",
2182          slot=self.cb_help_browse, tip="browse Message Board")
2183      act6 = self.createAction("web: gltsym and stim times",
2184          slot=self.cb_help_browse, tip="2004 update describing GLT usage")
2185
2186      self.addActions(self.gvars.Menu_browse, [act1, act2, act3, act4, act5,
2187                                               act6])
2188
2189      self.gvars.act_browse_all_progs = act1
2190      self.gvars.act_browse_AP_help   = act2
2191      self.gvars.act_browse_class_notes = act3
2192      self.gvars.act_browse_SS_tutor  = act4
2193      self.gvars.act_browse_MB        = act5
2194      self.gvars.act_browse_D2004_glt = act6
2195
2196      actHelpAbout = self.createAction("about uber_subject.py",
2197          slot=self.cb_help_about, tip="about uber_subject.py")
2198      self.addActions(self.gvars.MBar_help, [actHelpAbout])
2199
2200   def add_tool_bar(self):
2201      self.gvars.toolbar = self.addToolBar('afni_proc.py')
2202      self.gvars.toolbar.addAction(self.gvars.act_show_ap)
2203      self.gvars.toolbar.addAction(self.gvars.act_exec_ap)
2204      self.gvars.toolbar.addAction(self.gvars.act_exec_proc)
2205
2206   def createAction(self, text, slot=None, shortcut=None, tip=None,
2207                    checkable=False, icon=None, signal="triggered()"):
2208      action = QtGui.QAction(text, self)
2209      if shortcut is not None:
2210         action.setShortcut(shortcut)
2211      if tip is not None:
2212         action.setToolTip(tip)
2213         action.setStatusTip(tip)
2214      if slot is not None:
2215         self.connect(action, QtCore.SIGNAL(signal), slot)
2216      if checkable:
2217         action.setCheckable(True)
2218      if icon is not None:
2219         action.setIcon(icon)
2220      return action
2221
2222   def addActions(self, target, actions):
2223      for action in actions:
2224         if action is None:
2225            target.addSeparator()
2226         else:
2227            target.addAction(action)
2228
2229   def add_status_bar(self):
2230      self.gvars.statusbar = self.statusBar()
2231      self.gvars.statusbar.showMessage("Ready")
2232
2233   def make_extra_widgets(self):
2234      """create - ULIB.TextWindow for help messages, AP result and warnings
2235                - gvars.browser for web links"""
2236
2237      # text window (if None (for now), new windows will be created)
2238      # for help text
2239      self.gvars.Text_help         = QLIB.TextWindow(parent=self)
2240      # for output messages from AP_Subject class processing
2241      self.gvars.Text_AP_result    = QLIB.TextWindow(parent=self)
2242      # to show applied subject variables
2243      self.gvars.Text_applied_vars = QLIB.TextWindow(parent=self)
2244
2245      # note whether we have a browser via import
2246      self.gvars.browser = None
2247      try:
2248         import webbrowser
2249         self.gvars.browser = webbrowser
2250         if self.verb > 1: print('++ have browser')
2251      except:
2252         if self.verb > 1: print('-- NO browser')
2253
2254   def open_web_site(self, site):
2255      if self.gvars.browser == None:
2256         QLIB.guiWarning('Error', 'no browser to use for site: %s'%site,self)
2257      else: self.gvars.browser.open(site)
2258
2259   def get_changed_attrs_string(self):
2260      """show changes from defaults
2261         note: this may depend on analysis type (surf? rest?)
2262      """
2263
2264      return self.svars.changed_attrs_str(USUBJ.g_sdef_strs,
2265                        skiplist=USUBJ.g_svars_not_opt, showskip=1)
2266
2267   def update_applied_vars_window(self, win=None, text='', title='', fname=''):
2268      """default window is Text_AP_result
2269         - if fname, read file
2270           else if text, use text
2271           else (likely case), show changed_attrs
2272      """
2273      if win: window = win
2274      else:   window = self.gvars.Text_applied_vars
2275
2276      if text == '': text = self.get_changed_attrs_string()
2277
2278      if title: window.setWindowTitle(title)
2279      if fname: # then read from file
2280         window.filename = fname
2281         window.readfile()
2282      else: window.editor.setText(text)
2283      window.show()
2284      window.raise_()
2285
2286   def update_AP_result_window(self, win=None, text='', title='', fname=''):
2287      """default window is Text_AP_result
2288         - if fname, read file
2289           else use text"""
2290      if win: window = win
2291      else:   window = self.gvars.Text_AP_result
2292
2293      if title: window.setWindowTitle(title)
2294      if fname: # then read from file
2295         window.filename = fname
2296         window.readfile()
2297      else: window.editor.setText(text)
2298      window.show()
2299      window.raise_()
2300
2301   def update_AP_error_window(self, text, title='ERROR - cannot proceed'):
2302      QLIB.guiError(title, text, self)
2303
2304   def update_AP_warn_window(self, text, title='WARNING'):
2305      QLIB.guiWarning(title, text, self)
2306
2307   def update_help_window(self, text, title=''):
2308      # if no permanent window, make a new one each time
2309      if self.gvars.Text_help == None:
2310         if self.verb > 2: print('++ opening new TextWindow')
2311         win = QLIB.TextWindow(text=text, title=title, parent=self)
2312         win.setAttribute(QtCore.Qt.WA_DeleteOnClose)
2313         win.show()
2314      else:
2315         if title != '': self.gvars.Text_help.setWindowTitle(title)
2316         self.gvars.Text_help.editor.setText(text)
2317         self.gvars.Text_help.show()
2318
2319   def cb_help_main(self):
2320      """display helpstr_usubj_gui in Text_Help window"""
2321      self.update_help_window(USUBJ.helpstr_usubj_gui,
2322                              title='uber_subject.py: main help')
2323
2324   def cb_help_about(self):
2325      """display version info in Text_Help window"""
2326      text = "uber_subject.py, version %s" % USUBJ.g_version
2327      QLIB.guiMessage('about uber_subject.py', text, self)
2328
2329   def cb_help_browse(self):
2330      obj = self.sender()
2331      if   obj == self.gvars.act_browse_all_progs:
2332         self.open_web_site('https://afni.nimh.nih.gov/pub/dist/doc'     \
2333                            '/htmldoc/programs/main_toc.html')
2334      elif obj == self.gvars.act_browse_AP_help:
2335         self.open_web_site('https://afni.nimh.nih.gov/pub/dist/doc'     \
2336                            '/htmldoc/programs/afni_proc.py_sphx.html')
2337      elif obj == self.gvars.act_browse_SS_tutor:
2338         self.open_web_site('https://afni.nimh.nih.gov/pub/dist/edu/data' \
2339                            '/CD.expanded/AFNI_data6/FT_analysis/tutorial')
2340      elif obj == self.gvars.act_browse_class_notes:
2341         self.open_web_site('https://afni.nimh.nih.gov/pub/dist/edu' \
2342                            '/latest/afni_handouts')
2343      elif obj == self.gvars.act_browse_MB:
2344         self.open_web_site('https://afni.nimh.nih.gov/afni/community/board')
2345      elif obj == self.gvars.act_browse_D2004_glt:
2346         self.open_web_site('https://afni.nimh.nih.gov/pub/dist/doc'     \
2347                            '/misc/Decon/DeconSummer2004.html')
2348      else: print('** cb_help_browse: invalid sender')
2349
2350   def update_svars_from_gui(self, warn=0):
2351      """set what we can, if warn, report error
2352         return 0 on success, 1 on error"""
2353
2354      # first process sid and gid
2355      if self.svars.is_empty('sid') or self.svars.is_empty('gid'):
2356         if warn: QLIB.guiError('Error',
2357                                "** subject and group IDs must be set", self)
2358         return 1
2359
2360      # then process tables
2361      if self.update_svars_from_tables(): return 1
2362
2363      # if still default, create new subj_dir name
2364      if self.set_sdir:
2365         # subj dir should read: subject_results/group.gA/subj.SUBJ
2366         sdir =  USUBJ.get_def_subj_path(gid=self.svars.gid, sid=self.svars.sid)
2367         if sdir != self.cvars.val('subj_dir'):
2368            if self.verb: print('-- setting subj_dir to %s' % sdir)
2369            self.set_cvar('subj_dir', sdir)
2370
2371      return 0
2372
2373   def cb_show_ap_command(self):
2374
2375      if self.update_svars_from_gui(warn=1): return
2376
2377      # if we have a subject directory, make backup scripts
2378      if self.cvars.is_non_trivial_dir('subj_dir'):
2379         self.set_cvar('copy_scripts', 'yes')
2380
2381      # create the subject, check warnings and either a command or errors
2382      self.apsubj = USUBJ.AP_Subject(self.svars, self.cvars)
2383      nwarn, wstr = self.apsubj.get_ap_warnings()
2384      status, mesg = self.apsubj.get_ap_command()
2385
2386      if status:        # then only mention errors
2387         self.update_AP_error_window(mesg)
2388         return
2389
2390      self.gvars.ap_status = 1       # now have afni_proc.py command script
2391      self.apsubj.write_ap_command()
2392
2393      fname = self.apsubj.subj_dir_filename('file_ap')
2394      if not fname:
2395         self.update_AP_warn_window('** no proc script to show')
2396         return
2397      elif not os.path.isfile(fname):
2398         self.update_AP_warn_window('** proc script not found\n' \
2399                                    '   (should be file %s)' % fname)
2400         return
2401
2402      self.update_applied_vars_window(title="Applied Variables")
2403
2404      self.update_AP_result_window(title="Success!  afni_proc.py command:",
2405                                   fname=fname)
2406      if nwarn > 0: self.update_AP_warn_window(wstr)
2407
2408      # if we have a subject dir that exists, save the US.py command there
2409      self.write_uber_subj_command()
2410
2411      self.gvars.act_exec_ap.setEnabled(True)
2412      self.gvars.act_exec_proc.setEnabled(True)
2413
2414   def write_uber_subj_command(self):
2415      sdir = self.apsubj.cvars.val('subj_dir')
2416      if os.path.isdir(sdir):
2417         sstr = self.make_uber_command()
2418         sid = self.apsubj.svars.val('sid')
2419         UTIL.write_text_to_file('%s/.orig.cmd.usubj.%s'%(sdir,sid), sstr)
2420
2421   def update_svars_from_tables(self):
2422      """get updates from the EPI, stim and gltsym tables
2423
2424         return 0 on success, 1 on error"""
2425
2426      if self.update_EPI_from_table(): return 1
2427      if self.update_stim_from_table(): return 1
2428      if self.update_gltsym_from_table(): return 1
2429
2430      return 0
2431
2432   def update_EPI_from_table(self):
2433
2434      # --------------------------------------------------
2435      # get the EPI directory and the number of rows
2436      # --> for each row append dir/entry to svars.epi
2437      dir = str(self.gvars.Label_epi_dir.text())
2438      table = self.gvars.Table_epi              # convenience
2439      nrows = table.rowCount()
2440      dlist = []
2441      for row in range(nrows):
2442         item = table.item(row, 1)
2443         dset = str(item.text())
2444         if dir and dir != '.': pre = '%s/' % dir
2445         else:                  pre = ''
2446         dlist.append('%s%s' % (pre, dset))
2447      self.svars.epi = dlist    # replace the old list
2448
2449      return 0
2450
2451   def update_stim_from_table(self):
2452      """have 5 columns: index, label, basis, type, file
2453
2454         note: index is used only as a sorting option
2455      """
2456
2457      # --------------------------------------------------
2458      # stim table
2459      dir = str(self.gvars.Label_stim_dir.text())
2460      table = self.gvars.Table_stim             # convenience
2461      nrows = table.rowCount()
2462      llist = []        # labels
2463      blist = []        # bases
2464      tlist = []        # types
2465      dlist = []        # dsets (files)
2466      for row in range(nrows):
2467         # get label, basis, stim file
2468         item = table.item(row, 1)
2469         llist.append(str(item.text()))
2470
2471         item = table.item(row, 2)
2472         blist.append(str(item.text()))
2473
2474         item = table.item(row, 3)
2475         tlist.append(str(item.text()))
2476
2477         item = table.item(row, 4)
2478         dset = str(item.text())
2479         if dir and dir != '.': pre = '%s/' % dir
2480         else:                  pre = ''
2481         dlist.append('%s%s' % (pre, dset))
2482      self.svars.stim_label = llist
2483      self.svars.stim_basis = blist
2484      self.svars.stim_type  = tlist
2485      self.svars.stim = dlist
2486
2487      return 0
2488
2489   def update_gltsym_from_table(self):
2490
2491      # --------------------------------------------------
2492      # gltsym table (get labels and GLTs)
2493      table = self.gvars.Table_gltsym           # convenience
2494      nrows = table.rowCount()
2495      llist = []
2496      dlist = []
2497      err = ''
2498      for row in range(nrows):
2499         # get label, gltsym
2500         item = table.item(row, 0)
2501         if item != None: lab = str(item.text())
2502         else:            lab = ''
2503         item = table.item(row, 1)
2504         if item != None: glt = str(item.text())
2505         else:            glt = ''
2506
2507         # nuke any leading 'SYM: ' from the glt
2508         if glt[0:5] == 'SYM: ': glt = glt[5:]
2509
2510         if lab == '' or glt == '':
2511            if lab != '':
2512               err += '** GLT #%d, skipping label without GLT...\n' % (row+1)
2513            if glt != '':
2514               err += '** GLT #%d, skipping GLT without label...\n' % (row+1)
2515            continue
2516
2517         llist.append(lab)
2518         dlist.append(glt)
2519
2520      if err != '':
2521         QLIB.guiError('GLT Errors', err, self)
2522         return 1
2523
2524      rv, rstr = self.run_GLTsymtest(dlist)
2525      if rv:
2526         QLIB.guiError('GLTsymtest errors', rstr, self)
2527         return 1
2528
2529      self.svars.gltsym_label = llist
2530      self.svars.gltsym = dlist
2531
2532      return 0
2533
2534   def run_GLTsymtest(self, glist):
2535      """run GLTsymtest, returning the number of errors and a display string"""
2536
2537      llist = self.svars.stim_label
2538
2539      if len(llist) == 0 and len(glist) == 0: return 0, ''
2540
2541      lstr = "'%s'" % ' '.join(llist)
2542      cmd = "GLTsymtest -badonly %s" % lstr
2543
2544      elist = []
2545      for gind, glt in enumerate(glist):
2546         cstr = "%s '%s'" % (cmd, glt)
2547
2548         st, so, se = UTIL.limited_shell_exec(cstr)
2549         if self.verb > 1:
2550            print("++ GLTsymtest command (stat %d):\n   %s" % (st, cstr))
2551         if len(so) > 0 and self.verb > 2:
2552            print("\n%s" % ('   ' + '\n   '.join(so)))
2553
2554         # if no errors, continue on
2555         if st == 0: continue
2556
2557         emesg = "** GLT ERROR for -gltsym %d: '%s'\n" % (gind+1, glt)
2558         for soline in so:
2559            if soline.startswith('** ERROR:'):
2560               emesg += ('%s\n' % soline[3:])
2561
2562         elist.append('%s' % emesg)
2563
2564      if len(elist) > 0:
2565         emesg = "valid GLT labels are: %s\n\n%s" % (lstr, '\n'.join(elist))
2566         if self.verb > 3: print('failure emesg: %s' % emesg)
2567      else:
2568         emesg = ''
2569
2570      return len(elist), emesg
2571
2572   def init_analysis_defaults(self):
2573      """initialize the svar default based on anal_type and anal_domain
2574      """
2575      atype = self.svars.val('anal_type')
2576      adomain = self.svars.val('anal_domain')
2577
2578      if atype == 'rest': cobj = USUBJ.g_rdef_strs
2579      else:               cobj = USUBJ.g_tdef_strs
2580      changestr = cobj.changed_attrs_str(self.svars, skiplist='name',
2581                                         showskip=0, showdel=0)
2582      self.apply_svars(cobj)
2583
2584      title = "Applied Variables: to type '%s', domain '%s'" % (atype, adomain)
2585      self.update_applied_vars_window(title=title, text=changestr)
2586
2587   def cb_exec_ap_command(self):
2588      """execute afni_proc.py command script"""
2589      self.exec_ap_command()
2590
2591   def exec_ap_command(self):
2592      """execute afni_proc.py command script
2593
2594         return 0 on success, 1 on error"""
2595
2596      # are we ready?
2597      fmesg = ''        # init fail message to empty
2598      if not self.apsubj:
2599         fmesg = '** no subject class for running AP command'
2600      elif self.gvars.ap_status < 1:
2601         fmesg = '** need to first generate command'
2602
2603      if fmesg != '':
2604         QLIB.guiError('Error', fmesg, self)
2605         return 1
2606
2607      status, mesg = self.apsubj.exec_ap_command()
2608      if status:
2609         QLIB.guiError('Error', mesg, self)
2610         return 1
2611
2612      self.update_AP_result_window(text=mesg,
2613                                   title="output text from afni_proc.py")
2614
2615      self.gvars.ap_status = 2
2616
2617      return 0
2618
2619   def cb_exec_proc_script(self):
2620      """execute the proc script in a new terminal window"""
2621      # if we are not ready, return
2622      fmesg = ''
2623      if not self.apsubj:
2624         fmesg = '** no subject class for running proc script'
2625      elif self.gvars.ap_status < 1:
2626         fmesg = '** need to first generate AP command and script'
2627
2628      if fmesg != '':
2629         QLIB.guiError('Error', fmesg, self)
2630         return
2631
2632      if self.gvars.ap_status == 1:
2633         print('===== ++++ ===== re-running ap_command....')
2634         # execute afni_proc.py command, return on failure
2635         if self.exec_ap_command(): return
2636
2637      # make sure there is a script to execute
2638
2639      fname = self.apsubj.subj_dir_filename('file_proc')
2640      if not fname:
2641         self.update_AP_warn_window('** proc file not set')
2642         return
2643      elif not os.path.isfile(fname):
2644         self.update_AP_warn_window('** proc file not found: %s\n' % fname)
2645         return
2646
2647      self.apsubj.nuke_old_results()    # get rid of any previous results
2648
2649      # start a new window?
2650      if self.gvars.valid('SPW'):
2651         self.gvars.SPW.close()
2652         del(self.gvars.SPW)
2653
2654      # actually run script, piping output if we have an output file
2655      sname = self.apsubj.rvars.file_proc
2656      oname = self.apsubj.rvars.output_proc
2657      command= 'tcsh -xef %s' % sname
2658      if oname: command = '%s |& tee %s' % (command, oname)
2659      self.gvars.SPW = QLIB.TcshCommandWindow(command,
2660                           dir=self.apsubj.cvars.subj_dir, parent=self)
2661      self.gvars.SPW.show()
2662      self.gvars.ap_status = 3
2663
2664   def cb_clear_options(self):
2665      """set cvars, svars from defaults and redisplay GUI
2666         EXCEPT: keep dataset fields from subject"""
2667
2668      svars = VO.VarsObject()
2669      for atr in ['sid', 'gid', 'anat', 'epi', 'stim']:
2670         svars.set_var(atr, self.svars.val(atr))
2671
2672      self.reset_vars(svars=svars, set_sdir=self.set_sdir)
2673
2674   def cb_clear_fields(self):
2675      """set cvars, svars from defaults and redisplay GUI"""
2676      self.reset_vars(set_sdir=self.set_sdir)
2677
2678   def cb_view(self):
2679      """create permanent windows with given text"""
2680      obj = self.sender()
2681
2682      if obj == self.gvars.act_view_vars:
2683         self.update_applied_vars_window(title="Applied Variables")
2684
2685      if obj == self.gvars.act_view_ap_cmd:
2686         self.show_static_file('file_ap', 'afni_proc.py script')
2687
2688      elif obj == self.gvars.act_view_proc:
2689         self.show_static_file('file_proc', 'proc script')
2690
2691      elif obj == self.gvars.act_view_outproc:
2692         self.show_static_file('output_proc', 'proc output')
2693
2694      elif obj == self.gvars.act_view_svars:
2695         sstr = self.svars.make_show_str('current subject', name=0)
2696         QLIB.static_TextWindow(title='subject vars', text=sstr, parent=self)
2697
2698      elif obj == self.gvars.act_view_cvars:
2699         sstr = self.cvars.make_show_str('current subject', name=0)
2700         QLIB.static_TextWindow(title='control vars', text=sstr, parent=self)
2701
2702      elif obj == self.gvars.act_view_rvars:
2703         if self.apsubj == None:
2704            QLIB.guiWarning('Error', '** result vars not yet set', self)
2705            return
2706         sstr = self.apsubj.rvars.make_show_str('current subject', name=0)
2707         QLIB.static_TextWindow(title='return vars', text=sstr, parent=self)
2708
2709      elif obj == self.gvars.act_view_uber_cmd:
2710         sstr = self.make_uber_command()
2711         QLIB.static_TextWindow(title='corresponding uber_subject.py command',
2712                                text=sstr, parent=self)
2713
2714      elif obj == self.gvars.act_view_gvars:
2715         sstr = self.gvars.make_show_str('GUI vars', name=0, all=1)
2716         QLIB.static_TextWindow(title='GUI vars', text=sstr, parent=self)
2717
2718   def make_uber_command(self):
2719      """generate a script that would invoke the uber_subject.py interface
2720         with subject and control vars set (and so fields filled)
2721
2722         Put any key elements in quotes:
2723            basis functions
2724            gltsym
2725      """
2726
2727      # first apply subject variables
2728      self.update_svars_from_gui()
2729
2730      cmd = 'uber_subject.py'
2731
2732      # control vars first
2733      prefix = ' \\\n    -cvar '     # append before next command
2734      for atr in self.cvars.attributes():
2735         if atr == 'name': continue     # skip
2736         if self.cvars.vals_are_equal(atr, USUBJ.g_cdef_strs): continue
2737         # show this one
2738         val = self.cvars.val(atr)
2739         if self.cvars.has_simple_type(atr):
2740            cmd += (prefix + '%s %s' % (atr, val))
2741         elif type(val) == list:
2742            cmd += (prefix + '%s %s' % (atr, ' '.join(val)))
2743         else:
2744            print('** make_uber_command: bad attr %s' % atr)
2745
2746      # then subject vars
2747      prefix = ' \\\n    -svar '     # append before next command
2748      for atr in self.svars.attributes():
2749         if atr == 'name': continue     # skip
2750         if self.svars.vals_are_equal(atr, USUBJ.g_sdef_strs): continue
2751         # show this one
2752         val = self.svars.val(atr)
2753
2754         # special cases first: stim_basis, gltsym
2755         if atr == 'gltsym':            # special case
2756            val = ["'%s'" % v for v in val]
2757            cmd += (prefix + '%s %s' % (atr, ' '.join(val)))
2758
2759         elif atr == 'stim_basis':      # special case
2760            val = ["'%s'" % v for v in val]
2761            cmd += (prefix + '%s %s' % (atr, ' '.join(val)))
2762
2763         elif self.svars.has_simple_type(atr):
2764            cmd += (prefix + '%s %s' % (atr, val))
2765         elif type(val) == list:
2766            cmd += (prefix + '%s %s' % (atr, ' '.join(val)))
2767         else:
2768            print('** make_uber_command: bad attr %s' % atr)
2769
2770      return UTIL.add_line_wrappers(cmd + '\n')
2771
2772   def show_static_file(self, var_name, var_desc):
2773      """display the var according to var_name
2774         - use description in var_desc if any error"""
2775      if self.apsubj == None:        # check for generated command
2776         self.update_AP_warn_window('** afni_proc.py command must be\n' \
2777                                    '   executed first')
2778         return
2779      file = self.apsubj.subj_dir_filename(var_name)
2780      if not file:
2781         self.update_AP_warn_window('** file not set: %s' % var_desc)
2782         return
2783      elif not os.path.isfile(file):
2784         self.update_AP_warn_window('** file not found: %s\n' \
2785                                    '   (used for %s)'%(file, var_desc))
2786         return
2787      else:
2788         title = '%s %s' % (var_desc, file)
2789         QLIB.static_TextWindow(title=title, fname=file, parent=self)
2790
2791   def cb_setStyle(self):
2792      text = ''
2793      try:
2794         sender = self.sender()
2795         text = str(sender.text())
2796         print('-- cb_setSyle: text = %s' % text)
2797         if text == 'Default': text = g_styles[g_style_index_def]
2798      except:
2799         print('** cb_setSyle: cannot get text')
2800         return
2801
2802      if text in g_styles:
2803         try:
2804            self.gvars.style = text
2805            QtGui.QApplication.setStyle(QtGui.QStyleFactory.create(text))
2806         except: print("** failed to set style '%s'" % text)
2807      else: print("** style '%s' not in style list" % text)
2808
2809   def set_cvar(self, name, newval):
2810      """if the value has changed (or is not a simple type), update it
2811         - use deepcopy (nuke it from orbit, it's the only way to be sure)"""
2812
2813      if not self.cvars.set_var(name, newval): return
2814
2815      # so the value has changed...
2816
2817      if self.verb > 3 : print("++ set_cvar: update [%s] to '%s'"%(name,newval))
2818
2819   def set_svar(self, name, newval):
2820      """if the value has changed (or is not a simple type), update it
2821         - use deepcopy (nuke it from orbit, it's the only way to be sure)"""
2822
2823      if not self.svars.set_var(name, newval): return
2824
2825      # so the value has changed...
2826
2827      if self.verb > 3 : print("++ set_svar: update [%s] to '%s'"%(name,newval))
2828
2829      self.gvars.ap_status = 0  # no longer ready for execution
2830      self.gvars.act_exec_ap.setEnabled(False)
2831      self.gvars.act_exec_proc.setEnabled(False)
2832
2833   def set_PushB_from_svar(self, vname):
2834      """set PushB_vname text from corresponding svars val"""
2835      pb = self.gvars.val('PushB_%s' % vname)
2836      btext = self.svars.val(vname)
2837
2838      if not pb:
2839         print('** invalid PushB %s for text %s' % (vname, text))
2840         return
2841      if not btext:
2842         print('** invalid svar %s for PushB' % vname)
2843         return
2844
2845      pb.setText(btext)
2846
2847   def apply_svars(self, svars=None):
2848      """apply to the svars object and to the gui
2849
2850         first init to defaults
2851         if svars is passed, make further updates"""
2852
2853      # merge with current vars and apply everything to GUI
2854      self.svars.merge(svars)
2855      for var in self.svars.attributes():
2856         self.apply_svar_in_gui(var)
2857
2858      if self.verb > 2: self.svars.show("post reset subject vars")
2859
2860   def apply_svar_in_gui(self, svar):
2861      """this is a single interface to apply any subject variable in the GUI
2862
2863         if a variable is not handled in the interface, ignore it
2864
2865         return 1 if processed
2866      """
2867
2868      rv = 1
2869      if   svar == 'uber_dir':             rv = 0       # todo
2870      elif svar == 'anal_type':   self.set_PushB_from_svar('anal_type')
2871      elif svar == 'anal_domain': self.set_PushB_from_svar('anal_domain')
2872      elif svar == 'blocks':
2873                                  bstr = ' '.join(self.svars.val('blocks'))
2874                                  self.gvars.Line_blocks.setText(bstr)
2875      elif svar == 'sid':         self.gvars.Line_sid.setText(self.svars.sid)
2876      elif svar == 'gid':         self.gvars.Line_gid.setText(self.svars.gid)
2877      elif svar == 'anat':        self.gvars.Line_anat.setText(self.svars.anat)
2878      elif svar == 'get_tlrc':
2879                                  obj = self.gvars.gbox_anat.checkBox
2880                                  obj.setChecked(self.svars.get_tlrc=='yes')
2881      elif svar == 'epi':         self.epi_list_to_table()
2882      elif svar == 'epi_wildcard':
2883                                  var = self.svars.epi_wildcard
2884                                  obj = self.gvars.gbox_epi.checkBox_wildcard
2885                                  obj.setChecked(var=='yes')
2886      elif svar == 'stim':        self.stim_list_to_table()
2887      elif svar == 'stim_wildcard':
2888                                  var = self.svars.stim_wildcard
2889                                  obj = self.gvars.gbox_stim.checkBox_wildcard
2890                                  obj.setChecked(var=='yes')
2891      elif svar == 'label':       self.stim_list_to_table()
2892      elif svar == 'basis':       self.stim_list_to_table()
2893      elif svar == 'stim_type':   self.stim_list_to_table()
2894
2895      elif svar == 'tcat_nfirst':
2896                                   obj = self.gvars.Line_tcat_nfirst
2897                                   obj.setText(self.svars.tcat_nfirst)
2898      elif svar == 'volreg_base':
2899                                   obj = self.gvars.Line_volreg_base
2900                                   obj.setText(self.svars.volreg_base)
2901      elif svar == 'blur_size':
2902                                   obj = self.gvars.Line_blur_size
2903                                   obj.setText(self.svars.blur_size)
2904      elif svar == 'motion_limit':
2905                                   obj = self.gvars.Line_motion_limit
2906                                   obj.setText(self.svars.motion_limit)
2907      elif svar == 'gltsym':       self.gltsym_list_to_table()
2908      elif svar == 'gltsym_label': self.gltsym_list_to_table()
2909
2910      elif svar == 'outlier_limit':
2911                                   obj = self.gvars.Line_outlier_limit
2912                                   obj.setText(self.svars.outlier_limit)
2913      elif svar == 'regress_jobs':
2914                                   obj = self.gvars.Line_regress_jobs
2915                                   obj.setText(self.svars.regress_jobs)
2916      elif svar == 'regress_GOFORIT':
2917                                   obj = self.gvars.Line_regress_GOFORIT
2918                                   obj.setText(self.svars.regress_GOFORIT)
2919      elif svar == 'regress_bandpass':
2920                          obj = self.gvars.Line_regress_bandpass
2921                          obj.setText(' '.join(self.svars.regress_bandpass))
2922      elif svar == 'regress_mot_deriv':
2923                          var = self.svars.regress_mot_deriv
2924                          obj = self.gvars.gbox_regress
2925                          obj.checkBox_mot_deriv.setChecked(var=='yes')
2926      elif svar == 'reml_exec':
2927                          var = self.svars.reml_exec
2928                          obj = self.gvars.gbox_regress
2929                          obj.checkBox_reml_exec.setChecked(var=='yes')
2930      elif svar == 'run_clustsim':
2931                          var = self.svars.run_clustsim
2932                          obj = self.gvars.gbox_regress
2933                          obj.checkBox_run_clustsim.setChecked(var=='yes')
2934      elif svar == 'compute_fitts':
2935                          var = self.svars.compute_fitts
2936                          obj = self.gvars.gbox_regress
2937                          obj.checkBox_compute_fitts.setChecked(var=='yes')
2938      elif svar == 'align_cost':
2939                          obj = self.gvars.Line_align_cost
2940                          obj.setText(self.svars.align_cost)
2941      elif svar == 'tlrc_base':
2942                          obj = self.gvars.Line_tlrc_base
2943                          obj.setText(self.svars.tlrc_base)
2944      elif svar == 'align_giant_move':
2945                          var = self.svars.align_giant_move
2946                          obj = self.gvars.gbox_align
2947                          obj.checkBox_align_giant_move.setChecked(var=='yes')
2948      elif svar == 'anat_has_skull':
2949                          var = self.svars.anat_has_skull
2950                          obj = self.gvars.gbox_anat
2951                          obj.checkBox_anat_has_skull.setChecked(var=='yes')
2952      elif svar == 'tlrc_ok_maxite':
2953                          var = self.svars.tlrc_ok_maxite
2954                          obj = self.gvars.gbox_tlrc
2955                          obj.checkBox_tlrc_ok_maxite.setChecked(var=='yes')
2956
2957      else:
2958         if self.verb > 1: print('** apply_svar_in_gui: unhandled %s' % svar)
2959         rv = 0
2960
2961      if rv and self.verb > 2: print('++ apply_svar_in_gui: process %s' % svar)
2962
2963      return rv
2964
2965   def apply_cvars(self, cvars=None):
2966      """apply to the cvars object and to the gui
2967
2968         first init to defaults
2969         if cvars is passed, make further updates"""
2970
2971      # merge with current vars and apply everything to GUI
2972      self.cvars.merge(cvars)
2973      self.set_cvar('verb', str(self.verb))     # to pass verb along
2974      for var in self.cvars.attributes():
2975         self.apply_cvar_in_gui(var)
2976
2977      if self.verb > 2: self.cvars.show("post reset control vars")
2978
2979   def apply_cvar_in_gui(self, cvar):
2980      """this is a single interface to apply any control variable in the GUI
2981
2982         if a variable is not handled in the interface, ignore it
2983
2984         return 1 if processed
2985      """
2986
2987      rv = 1
2988      if   cvar == 'uber_dir':             rv = 0       # todo
2989      else:
2990         if self.verb > 1: print('** apply_cvar_in_gui: unhandled %s' % cvar)
2991         rv = 0
2992
2993      if rv and self.verb > 2: print('++ apply_cvar_in_gui: process %s' % cvar)
2994
2995      return rv
2996
2997# --- post SingleSubjectWindow class
2998
2999# ===========================================================================
3000# help strings
3001
3002g_help_init = """
3003Analysis initialization:
3004
3005   goals:
3006
3007      1. specify analysis type
3008      2. specify data domain
3009      3. specify main processing blocks
3010
3011   description:
3012
3013      Specifying the analysis type and data domain allows for more appropriate
3014      initialization of processing defaults.
3015
3016      Setting the 'type' has no effect until the 'APPLY' button is pressed, at
3017      which point the relevant defaults are changed and applied.
3018
3019      This implies that 'analysis initialization' should be done first (as one
3020      might expect), otherwise it could undo user changes.  For example, if
3021      the user sets the motion censor limit to 0.7 and then initializes the
3022      'type' to "rest", the 0.7 will be changed to 0.2, the default for resting
3023      state analysis.
3024"""
3025
3026g_help_anat = """
3027Specifying the anatomical dataset:
3028
3029   goals:
3030
3031      1. choose an anatomical dataset
3032      2. decide whether to include a copy of an existing +tlrc version
3033
3034   description:
3035
3036      Use 'browse anat' to pick an anatomical dataset that corresponds to
3037      the EPI datasets.  If 'include copy' is set, then if the anat is in
3038      +orig space and a +tlrc version already exists (from either a manual
3039      transformation or @auto_tlrc), the +tlrc version will be included.
3040
3041   typical use in processing:
3042
3043      1. copy anat (and possibly +tlrc version) into results directory
3044      2. if no +tlrc anatomy, create one via @auto_tlrc
3045      3. align EPI to anat (via align_epi_anat.py)
3046      4. transform EPI to +tlrc space (according to anat transformation)
3047
3048      note: the EPI transformations are applied in the 'volreg' block
3049"""
3050
3051g_help_epi = """
3052Specifying the EPI datasets:
3053
3054   goals:
3055
3056      1. choose a set of EPI datasets (from a single directory)
3057      2. decide whether to use the 'wildcard form' in the afni_proc.py
3058         command (rather than listing individual EPI datasets)
3059
3060   description:
3061
3062      Use 'browse EPI' to choose a list of EPI datasets from some directory.
3063      When the names are chosen, the directory will be separated from the
3064      dataset names, with the resulting names formed as a wildcard string.
3065      If the names do not have a fixed prefix and suffix, the wildcard string
3066      will probably not be appropriate.
3067
3068      The default order is the 'scan index' order, if indices are found.
3069      The datasets can be sored by either column by clicking on the column
3070      header (e.g. 'scan index').
3071
3072   typical use in processing:
3073
3074      1. remove pre-steady state TRs (specified farther down the GUI)
3075      2. pre-process the EPI: time shift, align to other EPI, align to anat,
3076           warp to standard space, blur, scale to percent of mean
3077      3. compute motion parameters based on EPI to EPI alignment (volreg)
3078      4. regress against model
3079
3080   file naming habits:
3081
3082      suggested example: epi_r07+orig.HEAD
3083
3084      It is a good habit to have the EPI dataset names be the same except for
3085      the run index.  It is also preferable (though not necessary) to zero-pad
3086      those indices so they have the same number of digits.  That makes the
3087      numeric scan order equal to the alphabetical order.
3088
3089      For example, ordering these files by run (scan) index gives:
3090
3091        epi_r1+orig.HEAD
3092        epi_r7+orig.HEAD
3093        epi_r9+orig.HEAD
3094        epi_r10+orig.HEAD
3095        epi_r11+orig.HEAD
3096
3097      But if they were sorted alphabetically (which would happen when using
3098      the wildcard form), the order would be (likely incorrect):
3099
3100        epi_r1+orig.HEAD
3101        epi_r10+orig.HEAD
3102        epi_r11+orig.HEAD
3103        epi_r7+orig.HEAD
3104        epi_r9+orig.HEAD
3105
3106     While this GUI would figure out the 'scan index' order of 1, 7, 9, 10, 11,
3107     it is a safer habit to zero pad the index values: 01, 07, 09, 10, 11.
3108     That would make the index order the same as the alphabetical order, which
3109     would also allow for the safe use of wildcards.  In that case, the order
3110     would be (for either scan index or alphabetical order):
3111
3112        epi_r01+orig.HEAD
3113        epi_r07+orig.HEAD
3114        epi_r09+orig.HEAD
3115        epi_r10+orig.HEAD
3116        epi_r11+orig.HEAD
3117
3118    Again, the zero padded order would be allow for wildcard use, specifying
3119    all five datasets by: "epi_r*+orig.HEAD".
3120
3121    Note that the wildcard use would also be inappropriate if there were some
3122    dataset that is not supposed to be used, such as if epi_r08+orig existed,
3123    but was for an aborted scan.
3124"""
3125
3126g_help_stim = """
3127Specifying the stimulus timing files:
3128
3129   goals:
3130
3131      1. choose a set of stimulus timing files (from a single directory)
3132      2. decide whether to use the 'wildcard form' in the afni_proc.py
3133         command (rather than listing individual files)
3134      3. choose a basis function (possibly for each timing file)
3135      4. possibly alter the stim types
3136
3137   description:
3138
3139      Use 'browse stim' to choose a list of timing files from some directory.
3140      When the names are chosen, the directory will be separated from each
3141      dataset name, with the resulting names formed as a wildcard string
3142      (which might not be appropriate).  The GUI will attempt to separate the
3143      file names into a list of: prefix, index, label, suffix.
3144      If this is possible, the index and label fields will be populated.
3145
3146      The default sort order is by 'index', if indices are found.  The files
3147      can be sorted by any column by clicking on the column header ('index').
3148
3149      If the wildcard form seems appropriate, it can be applied in the
3150      afni_proc.py script by setting 'use wildcard form'.
3151
3152      A basis functions will applied to each timing file (stimulus class).
3153      They can all be set at once via 'init basis funcs', or they can be
3154      modified individually in the table.
3155
3156      A stimulus type will applied to each file.  They can be initialzed at
3157      once via 'init file types', or they can be modified individually in the
3158      table.  The stimulus file types are shown in the 'choose' menu and
3159      correspond to those in afni_proc.py.
3160
3161      Most types are for timing files, while 'file' is for a simple regressor.
3162
3163   ** Note: no stimulus should be given during the pre-steady state TRs.
3164            The stimulus times should match times after the pre-SS TRs that
3165            are removed from the EPI data.
3166            Similarly, 'file' types should be of length #TRs minus all #pre-SS.
3167
3168   typical use in processing:
3169
3170      1. use the timing files and basis functions to create regressors of
3171         interest to be used in the regress processing block (by 3dDeconvolve).
3172      2. if the basis functions are fixed shapes (e.g. GAM/BLOCK), generate
3173         'ideal' curves per stimulus class (from the X-matrix columns)
3174      3. the 'type' entries control the -stim_times* or -stim_file options in
3175         the 3dDeconvolve command:
3176
3177            times       : -stim_times
3178            AM1         : -stim_times_AM1
3179            AM2         : -stim_times_AM2
3180            IM          : -stim_times_IM
3181            file        : -stim_file
3182
3183         See the '-regress_stim_types' option from afni_proc.py or the given
3184         options from 3dDeconvolve for more details.
3185
3186   file naming habits:
3187
3188      suggested example: stim_07_pizza.txt
3189
3190      a. To group files together, start them with the same prefix.
3191      b. To order them expectedly, add a zero-padded (as needed) index number,
3192         so the numerical order matches the alphabetical order.
3193      c. To be clear about what files they are, add the exact label that should
3194         be used in the 3dDeconvolve command.
3195      d. Optionally, add a useful file suffix.
3196
3197      See the 'help: EPI' section for details about numerical vs. alphabetical
3198      sorting and wildcard use.
3199
3200      Other reasonable naming examples:
3201
3202            stim7_pizza.txt
3203            7pizza
3204            stim07.1D
3205
3206      Bad examples:
3207
3208            faces.txt    (alphabetical order comes from labels)
3209            stim7        (it is not clear what stimulus class this is)
3210"""
3211
3212g_help_gltsym = """
3213
3214   goals:
3215
3216      1. specify symbolic GLTs (general linear tests)
3217      2. specify corresponding labels (used for sub-brick selection in results)
3218
3219   description:
3220
3221      To add a symbolic GLT, either "insert glt row" to add a blank row to the
3222      table, or "init with glt examples" to add some samples from the above
3223      stimulus list.  Since these are symbolic GLTs, they should be expressed
3224      as computations on the stimulus labels.
3225
3226      If the table is initialized with examples, GLT labels.
3227
3228      buttons:
3229
3230         insert glt row         : append a blank row
3231         init with glt examples : initialize table with GLTs from stim labels
3232         resize glt table       : delete blank rows (neither label nor GLT)
3233         clear glt table        : delete all table rows
3234         help: gltsym           : show this help
3235
3236   typical use in processing:
3237
3238      GLTs in this table will be passed along directly to the 3dDeconvolve
3239      command in the single subject processing script.  Each GLT label will
3240      become the sub-brick label of the resulting stats dataset, output from
3241      the linear regression.
3242
3243      For example, suppose one has stimulus labels: houses, faces and donuts.
3244      One might want to compare houses to donuts, or create a contrast which
3245      compares donuts with the mean of houses and faces.  Such labels and
3246      symbolic GLTs could be:
3247
3248         label          symbolic GLT
3249         -----          ------------
3250         H-D            houses -donuts
3251         D-HF           donuts -0.5*houses -0.5*faces
3252
3253      These would eventually be given to 3dDeconvolve as:
3254
3255        -gltsym 'SYM: houses -donuts' -glt_label 1 H-D
3256        -gltsym 'SYM: donuts -0.5*houses -0.5*faces' -glt_label 2 D-HF
3257
3258      Note that the leading SYM: in the symbolic GLT is applied by the program,
3259      and need not be specified by the user.
3260"""
3261
3262# this example should be displayed if we do not yet have stim labels set
3263g_help_gltsym_eg = """
3264"""
3265
3266g_help_eg = """
3267   goals:
3268
3269   description:
3270
3271"""
3272
3273# end: help strings
3274# ===========================================================================
3275
3276def main():
3277
3278   print('** this is not a main program')
3279   return 1
3280
3281if __name__ == '__main__':
3282   sys.exit(main())
3283