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