7171args: Argument string that is passed to dcm2niix. Click [Test] and see the terminal output for usage
7272 Tip: SPM users may want to use '-z n', which produces unzipped NIfTI's
7373
74- meta: The file extensions of the associated / equally named (meta)data sourcefiles that are copied over as
74+ meta: The file extensions of the associated / equally named (meta)data source files that are copied over as
7575 BIDS (sidecar) files, such as ['.json', '.tsv', '.tsv.gz']. You can use this to enrich json sidecar files,
7676 or add data that is not supported by this plugin
7777
@@ -91,7 +91,6 @@ def __init__(self, minsize: bool=True, ncols: int=0, nrows: int=0):
9191 self .horizontalHeader ().setVisible (False )
9292 self .verticalHeader ().setVisible (False )
9393 self .verticalHeader ().setDefaultSectionSize (ROW_HEIGHT )
94- self .setMinimumHeight (2 * (ROW_HEIGHT + 8 ))
9594 self .setSizeAdjustPolicy (QtWidgets .QAbstractScrollArea .SizeAdjustPolicy .AdjustToContents )
9695 if minsize :
9796 self .setSizePolicy (QtWidgets .QSizePolicy .Policy .Expanding , QtWidgets .QSizePolicy .Policy .Minimum )
@@ -101,6 +100,7 @@ def __init__(self, minsize: bool=True, ncols: int=0, nrows: int=0):
101100 self .setColumnCount (ncols )
102101 if nrows :
103102 self .setRowCount (nrows )
103+ self .resizeRowsToContents ()
104104
105105
106106class MyQTableItem (QTableWidgetItem ):
@@ -168,7 +168,7 @@ def __init__(self, bidsfolder: Path, input_bidsmap: BidsMap, template_bidsmap: B
168168 """The bidsmap from which new data type run-items are taken"""
169169 self .datasaved = datasaved
170170 """True if data has been saved on disk"""
171- self .dataformats = [dataformat .dataformat for dataformat in input_bidsmap .dataformats if input_bidsmap .dir (dataformat )]
171+ self .dataformats = [dataformat .dataformat for dataformat in input_bidsmap .dataformats if input_bidsmap .dir (dataformat ) and dataformat . dataformat ]
172172 self .bidsignore : list [str ] = input_bidsmap .options ['bidsignore' ]
173173 self .unknowndatatypes : list [str ] = input_bidsmap .options ['unknowntypes' ]
174174 self .ignoredatatypes : list [str ] = input_bidsmap .options ['ignoretypes' ]
@@ -1146,6 +1146,10 @@ def __init__(self, runitem: RunItem, bidsmap: BidsMap, template_bidsmap: BidsMap
11461146 meta_table .setToolTip (f"The key-value pair that will be appended to the (e.g. dcm2niix-produced) json sidecar file" )
11471147
11481148 # Set up the events tables
1149+ self .events_settings = events_settings = self .setup_table (events_data .get ('settings' , []), 'events_settings' )
1150+ events_settings .cellChanged .connect (self .events_settings2run )
1151+ events_settings .setToolTip (f"Settings for parsing the input table from the source file" )
1152+ events_settings .setStyleSheet ('QTableView::item {border-right: 1px solid #d6d9dc;}' )
11491153 inspect_button = QPushButton ('Source' )
11501154 inspect_button .setToolTip ('TODO' )
11511155 inspect_button .clicked .connect (self .inspect_sourcefile )
@@ -1174,7 +1178,7 @@ def __init__(self, runitem: RunItem, bidsmap: BidsMap, template_bidsmap: BidsMap
11741178 events_columns .horizontalHeader ().setVisible (True )
11751179 events_columns .setStyleSheet ('QTableView::item {border-right: 1px solid #d6d9dc;}' )
11761180 log_table_label = QLabel ('Log data' )
1177- log_table = self .setup_table (events_data .get ('log_table' ,[]), 'log_table' , minsize = False )
1181+ self . log_table = log_table = self .setup_table (events_data .get ('log_table' ,[]), 'log_table' , minsize = False )
11781182 log_table .setShowGrid (True )
11791183 log_table .horizontalHeader ().setVisible (True )
11801184 log_table .setToolTip (f"The raw stimulus presentation data that is parsed from the log file" )
@@ -1197,12 +1201,16 @@ def __init__(self, runitem: RunItem, bidsmap: BidsMap, template_bidsmap: BidsMap
11971201 sourcebox .setLayout (layout1 )
11981202
11991203 layout1_ = QVBoxLayout ()
1200- layout1_ .addWidget (inspect_button , alignment = QtCore .Qt .AlignmentFlag .AlignRight )
1204+ layout1_header = QHBoxLayout ()
1205+ layout1_header .addWidget (events_settings )
1206+ layout1_header .addStretch ()
1207+ layout1_header .addWidget (inspect_button )
1208+ layout1_ .addLayout (layout1_header )
12011209 layout1_ .addWidget (log_table_label )
12021210 layout1_ .addWidget (log_table )
1203- self .events_inbox = events_inbox = QGroupBox (f"{ self .dataformat } input data" )
1204- events_inbox .setSizePolicy (sizepolicy )
1205- events_inbox .setLayout (layout1_ )
1211+ self .events_inputbox = events_inputbox = QGroupBox (f"{ self .dataformat } input data" )
1212+ events_inputbox .setSizePolicy (sizepolicy )
1213+ events_inputbox .setLayout (layout1_ )
12061214
12071215 self .arrow = arrow = QLabel ()
12081216 arrow .setPixmap (QtGui .QPixmap (str (RIGHTARROW )).scaled (30 , 30 , QtCore .Qt .AspectRatioMode .KeepAspectRatio , QtCore .Qt .TransformationMode .SmoothTransformation ))
@@ -1251,10 +1259,10 @@ def __init__(self, runitem: RunItem, bidsmap: BidsMap, template_bidsmap: BidsMap
12511259 layout_tables .addWidget (arrow )
12521260 layout_tables .addWidget (bidsbox )
12531261 if events_data :
1254- layout_tables .addWidget (events_inbox )
1262+ layout_tables .addWidget (events_inputbox )
12551263 layout_tables .addWidget (events_editbox )
12561264 layout_tables .addWidget (eventsbox )
1257- events_inbox .hide ()
1265+ events_inputbox .hide ()
12581266 events_editbox .hide ()
12591267
12601268 # Set-up buttons
@@ -1354,12 +1362,12 @@ def fill_table(self, table: MyQTable, data: list):
13541362 for j , item in enumerate (row ):
13551363 itemvalue = item ['value' ]
13561364
1357- if tablename == 'bids' and isinstance (itemvalue , list ):
1365+ if tablename in ( 'bids' , 'events_settings' ) and isinstance (itemvalue , list ):
13581366 dropdown = QComboBox ()
13591367 dropdown .addItems (itemvalue [0 :- 1 ])
13601368 dropdown .setCurrentIndex (itemvalue [- 1 ])
1361- dropdown .currentIndexChanged .connect (partial (self .bids2run , i , j ))
1362- if j == 0 :
1369+ dropdown .currentIndexChanged .connect (partial (self .bids2run if tablename == 'bids' else self . events_settings2run , i , j ))
1370+ if tablename == 'bids' and j == 0 :
13631371 dropdown .setToolTip (get_entityhelp (key ))
13641372 table .setCellWidget (i , j , self .spacedwidget (dropdown ))
13651373
@@ -1482,6 +1490,11 @@ def run2data(self) -> tuple:
14821490 [{'value' : 'units/sec' , 'editable' : False }, {'value' : runitem .events ['time' ]['unit' ], 'editable' : True }],
14831491 [{'value' : 'start' , 'editable' : False }, {'value' : runitem .events ['time' ]['start' ], 'editable' : True }]]
14841492
1493+ # Set up the data for the events settings
1494+ events_data ['settings' ] = []
1495+ for key , value in runitem .events .get ('settings' , {}).items ():
1496+ events_data ['settings' ].append ([{'value' : key , 'editable' : True }, {'value' : value , 'editable' : True }])
1497+
14851498 # Set up the data for the events conditions / row groups
14861499 events_data ['rows' ] = [[{'value' : 'condition' , 'editable' : False }, {'value' : 'output column' , 'editable' : False }]]
14871500 for condition in runitem .events .get ('rows' ) or []:
@@ -1517,8 +1530,7 @@ def properties2run(self, rowindex: int, colindex: int):
15171530 if colindex == 1 :
15181531 key = self .properties_table .item (rowindex , 0 ).text ().strip ()
15191532 value = self .properties_table .item (rowindex , 1 ).text ().strip ()
1520- oldvalue = self .target_run .properties .get (key )
1521- if oldvalue is None :
1533+ if (oldvalue := self .target_run .properties .get (key )) is None :
15221534 oldvalue = ''
15231535
15241536 # Only if cell was changed, update
@@ -1541,8 +1553,7 @@ def attributes2run(self, rowindex: int, colindex: int):
15411553 if colindex == 1 :
15421554 key = self .attributes_table .item (rowindex , 0 ).text ().strip ()
15431555 value = self .attributes_table .item (rowindex , 1 ).text ()
1544- oldvalue = self .target_run .attributes .get (key )
1545- if oldvalue is None :
1556+ if (oldvalue := self .target_run .attributes .get (key )) is None :
15461557 oldvalue = ''
15471558
15481559 # Only if cell was changed, update
@@ -1567,11 +1578,9 @@ def bids2run(self, rowindex: int, colindex: int):
15671578 if hasattr (self .bids_table .cellWidget (rowindex , 1 ), 'spacedwidget' ):
15681579 dropdown = self .bids_table .cellWidget (rowindex , 1 ).spacedwidget
15691580 value = [dropdown .itemText (n ) for n in range (len (dropdown ))] + [dropdown .currentIndex ()]
1570- oldvalue = self .target_run .bids .get (key )
15711581 else :
15721582 value = self .bids_table .item (rowindex , 1 ).text ().strip ()
1573- oldvalue = self .target_run .bids .get (key )
1574- if oldvalue is None :
1583+ if (oldvalue := self .target_run .bids .get (key )) is None :
15751584 oldvalue = ''
15761585
15771586 # Only if cell was changed, update
@@ -1602,10 +1611,7 @@ def meta2run(self, rowindex: int, colindex: int):
16021611
16031612 key = self .meta_table .item (rowindex , 0 ).text ().strip ()
16041613 value = self .meta_table .item (rowindex , 1 ).text ().strip ()
1605- oldvalue = self .target_run .meta .get (key )
1606- if oldvalue is None :
1607- oldvalue = ''
1608- if value != oldvalue :
1614+ if value != (oldvalue := self .target_run .meta .get (key )):
16091615 # Replace the (dynamic) value
16101616 if '<<' not in value or '>>' not in value :
16111617 value = self .datasource .dynamicvalue (value , cleanup = False )
@@ -1663,6 +1669,34 @@ def events_time2run(self, rowindex: int, colindex: int):
16631669 self .fill_table (self .events_time , events_data ['time' ])
16641670 self .fill_table (self .events_table , events_data ['table' ])
16651671
1672+ def events_settings2run (self , rowindex : int , colindex : int ):
1673+ """Events settings table has been changed. Read the data from the event 'settings' table and, if OK, update the target run"""
1674+
1675+ key = self .events_settings .item (rowindex , 0 ).text ().strip ()
1676+ if hasattr (self .events_settings .cellWidget (rowindex , 1 ), 'spacedwidget' ):
1677+ dropdown = self .events_settings .cellWidget (rowindex , 1 ).spacedwidget
1678+ value = [dropdown .itemText (n ) for n in range (len (dropdown ))] + [dropdown .currentIndex ()]
1679+ else :
1680+ value = self .events_settings .item (rowindex , 1 ).text ().strip ()
1681+ if (oldvalue := self .target_run .events ['settings' ].get (key )) is None :
1682+ oldvalue = ''
1683+
1684+ # Only if cell was changed, update
1685+ if key and value != oldvalue :
1686+ # Validate user input against BIDS or replace the (dynamic) bids-value if it is a run attribute
1687+ if isinstance (value , str ) and ('<<' not in value or '>>' not in value ):
1688+ value = bids .sanitize (self .datasource .dynamicvalue (value ))
1689+ self .events_settings .blockSignals (True )
1690+ self .events_settings .item (rowindex , 1 ).setText (value )
1691+ self .events_settings .blockSignals (False )
1692+ LOGGER .verbose (f"User sets events['settings']['{ key } '] from '{ oldvalue } ' to '{ value } ' for { self .target_run } " )
1693+ self .target_run .events ['settings' ][key ] = value
1694+
1695+ # Refresh the log and events tables
1696+ _ ,_ ,_ ,_ ,events_data = self .run2data ()
1697+ self .fill_table (self .log_table , events_data ['log_table' ])
1698+ self .fill_table (self .events_table , events_data ['table' ])
1699+
16661700 def events_rows2run (self , rowindex : int , colindex : int ):
16671701 """Events value has been changed. Read the data from the event 'rows' table and, if OK, update the target run"""
16681702
@@ -1693,7 +1727,7 @@ def events_rows2run(self, rowindex: int, colindex: int):
16931727 # Refresh the events tables, i.e. delete empty rows or add a new row if a key is defined on the last row
16941728 _ ,_ ,_ ,_ ,events_data = self .run2data ()
16951729 self .fill_table (self .events_table , events_data ['table' ])
1696- self .fill_table (self .events_rows , events_data ['rows' ])
1730+ self .fill_table (self .events_rows , events_data ['rows' ])
16971731
16981732 def events_columns2run (self , rowindex : int , colindex : int ):
16991733 """Events value has been changed. Read the data from the event 'columns' table and, if OK, update the target run"""
@@ -1732,7 +1766,7 @@ def edit_events(self):
17321766 self .sourcebox .hide ()
17331767 self .arrow .hide ()
17341768 self .bidsbox .hide ()
1735- self .events_inbox .show ()
1769+ self .events_inputbox .show ()
17361770 self .events_editbox .show ()
17371771 self .edit_button .hide ()
17381772 self .done_button .show ()
@@ -1743,7 +1777,7 @@ def done_events(self):
17431777 self .sourcebox .show ()
17441778 self .arrow .show ()
17451779 self .bidsbox .show ()
1746- self .events_inbox .hide ()
1780+ self .events_inputbox .hide ()
17471781 self .events_editbox .hide ()
17481782 self .edit_button .show ()
17491783 self .done_button .hide ()
@@ -1856,6 +1890,7 @@ def reset(self, refresh: bool=False):
18561890 self .fill_table (self .bids_table , bids_data )
18571891 self .fill_table (self .meta_table , meta_data )
18581892 if events_data :
1893+ self .fill_table (self .events_settings , events_data ['settings' ])
18591894 self .fill_table (self .events_time , events_data ['time' ])
18601895 self .fill_table (self .events_rows , events_data ['rows' ])
18611896 self .fill_table (self .events_columns , events_data ['columns' ])
@@ -1940,20 +1975,20 @@ def inspect_sourcefile(self, rowindex: int=None, colindex: int=None):
19401975 QtGui .QDesktopServices .openUrl (QtCore .QUrl .fromLocalFile (str (Path (self .target_run .provenance ).parent )))
19411976
19421977 @staticmethod
1943- def spacedwidget (alignedwidget , align = 'left' ):
1978+ def spacedwidget (childwidget , align = 'left' ):
19441979 """Place the widget in a QHBoxLayout and add a stretcher next to it. Return the widget as widget.spacedwidget"""
19451980
19461981 widget = QtWidgets .QWidget ()
19471982 layout = QHBoxLayout ()
19481983 if align != 'left' :
19491984 layout .addStretch ()
1950- layout .addWidget (alignedwidget )
1985+ layout .addWidget (childwidget )
19511986 else :
1952- layout .addWidget (alignedwidget )
1987+ layout .addWidget (childwidget )
19531988 layout .addStretch ()
19541989 layout .setContentsMargins (0 , 0 , 0 , 0 )
19551990 widget .setLayout (layout )
1956- widget .spacedwidget = alignedwidget
1991+ widget .spacedwidget = childwidget
19571992 return widget
19581993
19591994 def get_help (self ):
@@ -2115,7 +2150,7 @@ def __init__(self, filename: Path):
21152150 if filename .name == 'DICOMDIR' :
21162151 LOGGER .bcdebug (f"Getting DICOM fields from { filename } will raise dcmread error below if pydicom => v3.0" )
21172152 text = str (dcmread (filename , force = True ))
2118- elif is_parfile (filename ) or filename .suffix .lower () in ('.spar' , '.txt' , '.text' , '.log' ):
2153+ elif is_parfile (filename ) or filename .suffix .lower () in ('.spar' , '.txt' , '.text' , '.log' , '.csv' , '.tsv' ):
21192154 text = filename .read_text ()
21202155 elif filename .suffix .lower () == '.7' :
21212156 try :
0 commit comments