Commit ac47aa77eea981a7abd7e4a94dff04c74bb2e16c
1 parent
23bc2c04
Exists in
master
- Added new class for storing markers
- General marker code cleanup - Marker's label decoupled from the target status
Showing
3 changed files
with
231 additions
and
186 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -828,5 +828,4 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit |
828 | 828 | |
829 | 829 | MARKER_FILE_MAGICK_STRING = "INVESALIUS3_MARKER_FILE_" |
830 | 830 | CURRENT_MARKER_FILE_VERSION = 0 |
831 | -WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss") + "|" +\ | |
832 | - _("Marker files (*.mks)|*.mks") | |
831 | +WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss") | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -280,7 +280,7 @@ class Viewer(wx.Panel): |
280 | 280 | Publisher.subscribe(self.HideAllMarkers, 'Hide all markers') |
281 | 281 | Publisher.subscribe(self.ShowAllMarkers, 'Show all markers') |
282 | 282 | Publisher.subscribe(self.RemoveAllMarkers, 'Remove all markers') |
283 | - Publisher.subscribe(self.RemoveMarker, 'Remove marker') | |
283 | + Publisher.subscribe(self.RemoveMultipleMarkers, 'Remove multiple markers') | |
284 | 284 | Publisher.subscribe(self.BlinkMarker, 'Blink Marker') |
285 | 285 | Publisher.subscribe(self.StopBlinkMarker, 'Stop Blink Marker') |
286 | 286 | Publisher.subscribe(self.SetNewColor, 'Set new color') |
... | ... | @@ -676,7 +676,7 @@ class Viewer(wx.Panel): |
676 | 676 | self.staticballs = [] |
677 | 677 | self.UpdateRender() |
678 | 678 | |
679 | - def RemoveMarker(self, index): | |
679 | + def RemoveMultipleMarkers(self, index): | |
680 | 680 | for i in reversed(index): |
681 | 681 | self.ren.RemoveActor(self.staticballs[i]) |
682 | 682 | del self.staticballs[i] | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | # detalhes. |
18 | 18 | #-------------------------------------------------------------------------- |
19 | 19 | |
20 | +import dataclasses | |
20 | 21 | from functools import partial |
21 | 22 | import itertools |
22 | 23 | import csv |
... | ... | @@ -1078,6 +1079,76 @@ class ObjectRegistrationPanel(wx.Panel): |
1078 | 1079 | |
1079 | 1080 | |
1080 | 1081 | class MarkersPanel(wx.Panel): |
1082 | + @dataclasses.dataclass | |
1083 | + class Marker: | |
1084 | + """Class for storing markers. @dataclass decorator simplifies | |
1085 | + setting default values, serialization, etc.""" | |
1086 | + x : float = 0 | |
1087 | + y : float = 0 | |
1088 | + z : float = 0 | |
1089 | + alpha : float = 0 | |
1090 | + beta : float = 0 | |
1091 | + gamma : float = 0 | |
1092 | + r : float = 0 | |
1093 | + g : float = 1 | |
1094 | + b : float = 0 | |
1095 | + size : int = 2 | |
1096 | + label : str = '*' | |
1097 | + x_seed : float = 0 | |
1098 | + y_seed : float = 0 | |
1099 | + z_seed : float = 0 | |
1100 | + is_target : int = 0 # is_target is int instead of boolean to avoid | |
1101 | + # problems with CSV export | |
1102 | + | |
1103 | + # x, y, z, alpha, beta, gamma can be jointly accessed as coord | |
1104 | + @property | |
1105 | + def coord(self): | |
1106 | + return list((self.x, self.y, self.z, self.alpha, self.beta, self.gamma),) | |
1107 | + | |
1108 | + @coord.setter | |
1109 | + def coord(self, new_coord): | |
1110 | + self.x, self.y, self.z, self.alpha, self.beta, self.gamma = new_coord | |
1111 | + | |
1112 | + # r, g, b can be jointly accessed as colour | |
1113 | + @property | |
1114 | + def colour(self): | |
1115 | + return list((self.r, self.g, self.b),) | |
1116 | + | |
1117 | + @colour.setter | |
1118 | + def colour(self, new_colour): | |
1119 | + self.r, self.g, self.b = new_colour | |
1120 | + | |
1121 | + # x_seed, y_seed, z_seed can be jointly accessed as seed | |
1122 | + @property | |
1123 | + def seed(self): | |
1124 | + return list((self.x_seed, self.y_seed, self.z_seed),) | |
1125 | + | |
1126 | + @seed.setter | |
1127 | + def seed(self, new_seed): | |
1128 | + self.x_seed, self.y_seed, self.z_seed = new_seed | |
1129 | + | |
1130 | + @classmethod | |
1131 | + def get_headers(cls): | |
1132 | + """Return the list of field names (headers) for exporting to csv.""" | |
1133 | + res = [field.name for field in dataclasses.fields(cls)] | |
1134 | + res.extend(['x_world', 'y_world', 'z_world', 'alpha_world', 'beta_world', 'gamma_world']) | |
1135 | + return res | |
1136 | + | |
1137 | + def get_values(self): | |
1138 | + """Return the list of values for exporting to csv.""" | |
1139 | + res = [] | |
1140 | + res.extend(dataclasses.astuple(self)) | |
1141 | + | |
1142 | + # Add world coordinates (in addition to the internal ones). | |
1143 | + position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( | |
1144 | + position=[self.x, self.y, self.z], | |
1145 | + orientation=[self.alpha, self.beta, self.gamma], | |
1146 | + ) | |
1147 | + res.extend(position_world) | |
1148 | + res.extend(orientation_world) | |
1149 | + | |
1150 | + return res | |
1151 | + | |
1081 | 1152 | def __init__(self, parent): |
1082 | 1153 | wx.Panel.__init__(self, parent) |
1083 | 1154 | try: |
... | ... | @@ -1093,8 +1164,7 @@ class MarkersPanel(wx.Panel): |
1093 | 1164 | self.current_coord = 0, 0, 0, 0, 0, 0 |
1094 | 1165 | self.current_angle = 0, 0, 0 |
1095 | 1166 | self.current_seed = 0, 0, 0 |
1096 | - self.list_coord = [] | |
1097 | - self.tgt_flag = self.tgt_index = None | |
1167 | + self.markers = [] | |
1098 | 1168 | self.nav_status = False |
1099 | 1169 | |
1100 | 1170 | self.marker_colour = const.MARKER_COLOUR |
... | ... | @@ -1155,11 +1225,15 @@ class MarkersPanel(wx.Panel): |
1155 | 1225 | self.lc.InsertColumn(2, 'Y') |
1156 | 1226 | self.lc.InsertColumn(3, 'Z') |
1157 | 1227 | self.lc.InsertColumn(4, 'ID') |
1228 | + self.lc.InsertColumn(5, 'Target') | |
1229 | + | |
1158 | 1230 | self.lc.SetColumnWidth(0, 28) |
1159 | 1231 | self.lc.SetColumnWidth(1, 50) |
1160 | 1232 | self.lc.SetColumnWidth(2, 50) |
1161 | 1233 | self.lc.SetColumnWidth(3, 50) |
1162 | 1234 | self.lc.SetColumnWidth(4, 60) |
1235 | + self.lc.SetColumnWidth(5, 60) | |
1236 | + | |
1163 | 1237 | self.lc.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnMouseRightDown) |
1164 | 1238 | self.lc.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemBlink) |
1165 | 1239 | self.lc.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.OnStopItemBlink) |
... | ... | @@ -1184,6 +1258,70 @@ class MarkersPanel(wx.Panel): |
1184 | 1258 | Publisher.subscribe(self.UpdateNavigationStatus, 'Navigation status') |
1185 | 1259 | Publisher.subscribe(self.UpdateSeedCoordinates, 'Update tracts') |
1186 | 1260 | |
1261 | + def __find_target_marker(self): | |
1262 | + """Return the index of the marker currently selected as target (there | |
1263 | + should be at most one). If there is no such marker, return -1.""" | |
1264 | + for i in range(len(self.markers)): | |
1265 | + if self.markers[i].is_target: | |
1266 | + return i | |
1267 | + | |
1268 | + return -1 | |
1269 | + | |
1270 | + def __get_selected_items(self): | |
1271 | + """ | |
1272 | + Returns a (possibly empty) list of the selected items in the list control. | |
1273 | + """ | |
1274 | + selection = [] | |
1275 | + | |
1276 | + next = self.lc.GetFirstSelected() | |
1277 | + | |
1278 | + while next != -1: | |
1279 | + selection.append(next) | |
1280 | + next = self.lc.GetNextSelected(next) | |
1281 | + | |
1282 | + return selection | |
1283 | + | |
1284 | + def __delete_multiple_markers(self, index): | |
1285 | + """ Delete multiple markers indexed by index. index must be sorted in | |
1286 | + the ascending order. | |
1287 | + """ | |
1288 | + for i in reversed(index): | |
1289 | + del self.markers[i] | |
1290 | + self.lc.DeleteItem(i) | |
1291 | + for n in range(0, self.lc.GetItemCount()): | |
1292 | + self.lc.SetItem(n, 0, str(n+1)) | |
1293 | + Publisher.sendMessage('Remove multiple markers', index=index) | |
1294 | + | |
1295 | + def __set_marker_as_target(self, idx): | |
1296 | + """Set marker indexed by idx as the new target. idx must be a valid index.""" | |
1297 | + # Find the previous target | |
1298 | + prev_idx = self.__find_target_marker() | |
1299 | + | |
1300 | + # If the new target is same as the previous do nothing. | |
1301 | + if prev_idx == idx: | |
1302 | + return | |
1303 | + | |
1304 | + # Unset the previous target | |
1305 | + if prev_idx != -1: | |
1306 | + self.markers[prev_idx].is_target = 0 | |
1307 | + self.lc.SetItemBackgroundColour(prev_idx, 'white') | |
1308 | + Publisher.sendMessage('Set target transparency', status=False, index=prev_idx) | |
1309 | + self.lc.SetItem(prev_idx, 5, "") | |
1310 | + | |
1311 | + # Set the new target | |
1312 | + self.markers[idx].is_target = 1 | |
1313 | + self.lc.SetItemBackgroundColour(idx, 'RED') | |
1314 | + self.lc.SetItem(idx, 5, _("Yes")) | |
1315 | + | |
1316 | + Publisher.sendMessage('Update target', coord=self.markers[idx].coord) | |
1317 | + Publisher.sendMessage('Set target transparency', status=True, index=idx) | |
1318 | + wx.MessageBox(_("New target selected."), _("InVesalius 3")) | |
1319 | + | |
1320 | + @staticmethod | |
1321 | + def __list_fiducial_labels(): | |
1322 | + """Return the list of marker labels denoting fucials.""" | |
1323 | + return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS))) | |
1324 | + | |
1187 | 1325 | def UpdateCurrentCoord(self, position): |
1188 | 1326 | self.current_coord = position |
1189 | 1327 | #self.current_angle = pubsub_evt.data[1][3:] |
... | ... | @@ -1203,7 +1341,7 @@ class MarkersPanel(wx.Panel): |
1203 | 1341 | # TODO: Enable the "Set as target" only when target is created with registered object |
1204 | 1342 | menu_id = wx.Menu() |
1205 | 1343 | edit_id = menu_id.Append(0, _('Edit ID')) |
1206 | - menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerId, edit_id) | |
1344 | + menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerLabel, edit_id) | |
1207 | 1345 | color_id = menu_id.Append(2, _('Edit color')) |
1208 | 1346 | menu_id.Bind(wx.EVT_MENU, self.OnMenuSetColor, color_id) |
1209 | 1347 | menu_id.AppendSeparator() |
... | ... | @@ -1223,54 +1361,29 @@ class MarkersPanel(wx.Panel): |
1223 | 1361 | def OnStopItemBlink(self, evt): |
1224 | 1362 | Publisher.sendMessage('Stop Blink Marker') |
1225 | 1363 | |
1226 | - def OnMenuEditMarkerId(self, evt): | |
1364 | + def OnMenuEditMarkerLabel(self, evt): | |
1227 | 1365 | list_index = self.lc.GetFocusedItem() |
1228 | - if evt == 'TARGET': | |
1229 | - id_label = evt | |
1230 | - else: | |
1231 | - id_label = dlg.ShowEnterMarkerID(self.lc.GetItemText(list_index, 4)) | |
1232 | - if id_label == 'TARGET': | |
1233 | - id_label = '*' | |
1234 | - wx.MessageBox(_("Invalid TARGET ID."), _("InVesalius 3")) | |
1235 | - | |
1236 | - # Add the new ID to exported list | |
1237 | - if len(self.list_coord[list_index]) > 8: | |
1238 | - self.list_coord[list_index][10] = str(id_label) | |
1366 | + if list_index != -1: | |
1367 | + new_label = dlg.ShowEnterMarkerID(self.lc.GetItemText(list_index, 4)) | |
1368 | + self.markers[list_index].label = str(new_label) | |
1369 | + self.lc.SetItem(list_index, 4, new_label) | |
1239 | 1370 | else: |
1240 | - self.list_coord[list_index][7] = str(id_label) | |
1241 | - | |
1242 | - self.lc.SetItem(list_index, 4, id_label) | |
1371 | + wx.MessageBox(_("No data selected."), _("InVesalius 3")) | |
1243 | 1372 | |
1244 | 1373 | def OnMenuSetTarget(self, evt): |
1245 | - if isinstance(evt, int): | |
1246 | - self.lc.Focus(evt) | |
1247 | - | |
1248 | - if self.tgt_flag: | |
1249 | - marker_id = '*' | |
1250 | - | |
1251 | - self.lc.SetItemBackgroundColour(self.tgt_index, 'white') | |
1252 | - Publisher.sendMessage('Set target transparency', status=False, index=self.tgt_index) | |
1253 | - self.lc.SetItem(self.tgt_index, 4, marker_id) | |
1254 | - | |
1255 | - # Add the new ID to exported list | |
1256 | - if len(self.list_coord[self.tgt_index]) > 8: | |
1257 | - self.list_coord[self.tgt_index][10] = marker_id | |
1258 | - else: | |
1259 | - self.list_coord[self.tgt_index][7] = marker_id | |
1260 | - | |
1261 | - self.tgt_index = self.lc.GetFocusedItem() | |
1262 | - self.lc.SetItemBackgroundColour(self.tgt_index, 'RED') | |
1263 | - | |
1264 | - Publisher.sendMessage('Update target', coord=self.list_coord[self.tgt_index][:6]) | |
1265 | - Publisher.sendMessage('Set target transparency', status=True, index=self.tgt_index) | |
1266 | - self.OnMenuEditMarkerId('TARGET') | |
1267 | - self.tgt_flag = True | |
1268 | - wx.MessageBox(_("New target selected."), _("InVesalius 3")) | |
1374 | + idx = self.lc.GetFocusedItem() | |
1375 | + if idx != -1: | |
1376 | + self.__set_marker_as_target(idx) | |
1377 | + else: | |
1378 | + wx.MessageBox(_("No data selected."), _("InVesalius 3")) | |
1269 | 1379 | |
1270 | 1380 | def OnMenuSetColor(self, evt): |
1271 | 1381 | index = self.lc.GetFocusedItem() |
1382 | + if index == -1: | |
1383 | + wx.MessageBox(_("No data selected."), _("InVesalius 3")) | |
1384 | + return | |
1272 | 1385 | |
1273 | - color_current = [self.list_coord[index][n] * 255 for n in range(6, 9)] | |
1386 | + color_current = [ch * 255 for ch in self.markers[index].colour] | |
1274 | 1387 | |
1275 | 1388 | color_new = dlg.ShowColorDialog(color_current=color_current) |
1276 | 1389 | |
... | ... | @@ -1280,29 +1393,28 @@ class MarkersPanel(wx.Panel): |
1280 | 1393 | # XXX: Seems like a slightly too early point for rounding; better to round only when the value |
1281 | 1394 | # is printed to the screen or file. |
1282 | 1395 | # |
1283 | - self.list_coord[index][6:9] = [round(s/255.0, 3) for s in color_new] | |
1396 | + self.markers[index].colour = [round(s/255.0, 3) for s in color_new] | |
1284 | 1397 | |
1285 | 1398 | Publisher.sendMessage('Set new color', index=index, color=color_new) |
1286 | 1399 | |
1287 | 1400 | def OnDeleteAllMarkers(self, evt=None): |
1288 | - if self.list_coord: | |
1289 | - if evt is None: | |
1290 | - result = wx.ID_OK | |
1291 | - else: | |
1292 | - # result = dlg.DeleteAllMarkers() | |
1293 | - result = dlg.ShowConfirmationDialog(msg=_("Remove all markers? Cannot be undone.")) | |
1401 | + if evt is None: | |
1402 | + result = wx.ID_OK | |
1403 | + else: | |
1404 | + result = dlg.ShowConfirmationDialog(msg=_("Remove all markers? Cannot be undone.")) | |
1294 | 1405 | |
1295 | - if result == wx.ID_OK: | |
1296 | - self.list_coord = [] | |
1297 | - Publisher.sendMessage('Remove all markers', indexes=self.lc.GetItemCount()) | |
1298 | - self.lc.DeleteAllItems() | |
1299 | - Publisher.sendMessage('Stop Blink Marker', index='DeleteAll') | |
1406 | + if result != wx.ID_OK: | |
1407 | + return | |
1408 | + | |
1409 | + if self.__find_target_marker() != -1: | |
1410 | + Publisher.sendMessage('Disable or enable coil tracker', status=False) | |
1411 | + if evt is not None: | |
1412 | + wx.MessageBox(_("Target deleted."), _("InVesalius 3")) | |
1300 | 1413 | |
1301 | - if self.tgt_flag: | |
1302 | - self.tgt_flag = self.tgt_index = None | |
1303 | - Publisher.sendMessage('Disable or enable coil tracker', status=False) | |
1304 | - if not hasattr(evt, 'data'): | |
1305 | - wx.MessageBox(_("Target deleted."), _("InVesalius 3")) | |
1414 | + self.markers = [] | |
1415 | + Publisher.sendMessage('Remove all markers', indexes=self.lc.GetItemCount()) | |
1416 | + self.lc.DeleteAllItems() | |
1417 | + Publisher.sendMessage('Stop Blink Marker', index='DeleteAll') | |
1306 | 1418 | |
1307 | 1419 | def OnDeleteMultipleMarkers(self, evt=None, label=None): |
1308 | 1420 | # OnDeleteMultipleMarkers is used for both pubsub and button click events |
... | ... | @@ -1310,9 +1422,8 @@ class MarkersPanel(wx.Panel): |
1310 | 1422 | |
1311 | 1423 | if not evt: # called through pubsub |
1312 | 1424 | index = [] |
1313 | - allowed_labels = itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS)) | |
1314 | - | |
1315 | - if label and (label in allowed_labels): | |
1425 | + | |
1426 | + if label and (label in self.__list_fiducial_labels()): | |
1316 | 1427 | for id_n in range(self.lc.GetItemCount()): |
1317 | 1428 | item = self.lc.GetItem(id_n, 4) |
1318 | 1429 | if item.GetText() == label: |
... | ... | @@ -1320,49 +1431,32 @@ class MarkersPanel(wx.Panel): |
1320 | 1431 | index = [self.lc.GetFocusedItem()] |
1321 | 1432 | |
1322 | 1433 | else: # called from button click |
1323 | - index = self.__getSelectedItems() | |
1434 | + index = self.__get_selected_items() | |
1324 | 1435 | |
1325 | - #TODO: Bug - when deleting multiple markers and target is not the first marker | |
1326 | 1436 | if index: |
1327 | - if self.tgt_flag and self.tgt_index == index[0]: | |
1328 | - self.tgt_flag = self.tgt_index = None | |
1437 | + if self.__find_target_marker() in index: | |
1329 | 1438 | Publisher.sendMessage('Disable or enable coil tracker', status=False) |
1330 | 1439 | wx.MessageBox(_("Target deleted."), _("InVesalius 3")) |
1331 | 1440 | |
1332 | - self.__deleteMultipleMarkers(index) | |
1441 | + self.__delete_multiple_markers(index) | |
1333 | 1442 | else: |
1334 | - wx.MessageBox(_("No data selected."), _("InVesalius 3")) | |
1335 | - | |
1336 | - def __deleteMultipleMarkers(self, index): | |
1337 | - """ Delete multiple markers indexed by index. index must be sorted in | |
1338 | - the ascending order. | |
1339 | - """ | |
1340 | - for i in reversed(index): | |
1341 | - del self.list_coord[i] | |
1342 | - self.lc.DeleteItem(i) | |
1343 | - for n in range(0, self.lc.GetItemCount()): | |
1344 | - self.lc.SetItem(n, 0, str(n+1)) | |
1345 | - Publisher.sendMessage('Remove marker', index=index) | |
1443 | + if evt: # Don't show the warning if called through pubsub | |
1444 | + wx.MessageBox(_("No data selected."), _("InVesalius 3")) | |
1346 | 1445 | |
1347 | 1446 | def OnCreateMarker(self, evt): |
1348 | 1447 | self.CreateMarker() |
1349 | 1448 | |
1350 | 1449 | def OnLoadMarkers(self, evt): |
1450 | + """Loads markers from file and appends them to the current marker list. | |
1451 | + The file should contain no more than a single target marker. Also the | |
1452 | + file should not contain any fiducials already in the list.""" | |
1351 | 1453 | filename = dlg.ShowLoadSaveDialog(message=_(u"Load markers"), |
1352 | 1454 | wildcard=const.WILDCARD_MARKER_FILES) |
1353 | 1455 | |
1354 | 1456 | if not filename: |
1355 | 1457 | return |
1356 | - | |
1357 | - if filename.lower().endswith('.mks'): | |
1358 | - wx.MessageBox(_(".mks files are no longer supported. Convert them to .mkss with the conversion tool."), _("InVesalius 3")) | |
1359 | - return | |
1360 | 1458 | |
1361 | - # Treat any extension othjer than .mks as 'new' format that has magick | |
1362 | - # string and version number | |
1363 | 1459 | try: |
1364 | - count_line = self.lc.GetItemCount() | |
1365 | - | |
1366 | 1460 | with open(filename, 'r') as file: |
1367 | 1461 | magick_line = file.readline() |
1368 | 1462 | assert magick_line.startswith(const.MARKER_FILE_MAGICK_STRING) |
... | ... | @@ -1371,39 +1465,22 @@ class MarkersPanel(wx.Panel): |
1371 | 1465 | wx.MessageBox(_("Unknown version of the markers file."), _("InVesalius 3")) |
1372 | 1466 | return |
1373 | 1467 | |
1374 | - # read lines from the file | |
1375 | 1468 | reader = csv.reader(file, dialect='markers_dialect') |
1376 | 1469 | next(reader) # skip the header line |
1377 | - content = [row for row in reader] | |
1378 | - | |
1379 | - # parse the lines and update the markers list | |
1380 | - for line in content: | |
1381 | - target = None | |
1382 | - | |
1383 | - coord = [float(s) for s in line[:6]] | |
1384 | - colour = [float(s) for s in line[12:15]] | |
1385 | - size = float(line[15]) | |
1386 | - marker_id = line[16] | |
1387 | - | |
1388 | - seed = [float(s) for s in line[17:20]] | |
1389 | - | |
1390 | - for i in const.BTNS_IMG_MARKERS: | |
1391 | - if marker_id in list(const.BTNS_IMG_MARKERS[i].values())[0]: | |
1392 | - Publisher.sendMessage('Load image fiducials', marker_id=marker_id, coord=coord) | |
1393 | - elif marker_id == 'TARGET': | |
1394 | - target = count_line | |
1395 | - | |
1396 | - target_id = line[20] | |
1397 | - | |
1398 | - self.CreateMarker(coord=coord, colour=colour, size=size, | |
1399 | - label=marker_id, target_id=target_id, seed=seed) | |
1400 | - | |
1401 | - # if there are multiple TARGETS will set the last one | |
1402 | - if target: | |
1403 | - self.OnMenuSetTarget(target) | |
1404 | - | |
1405 | - count_line += 1 | |
1406 | - | |
1470 | + | |
1471 | + # Read the data lines and create markers | |
1472 | + for line in reader: | |
1473 | + marker = self.Marker(*line[:-6]) # Discard the last 6 fields (the world coordinates) | |
1474 | + self.CreateMarker(coord=marker.coord, colour=marker.colour, size=marker.size, | |
1475 | + label=marker.label, is_target=0, seed=marker.seed) | |
1476 | + | |
1477 | + if marker.label in self.__list_fiducial_labels(): | |
1478 | + Publisher.sendMessage('Load image fiducials', label=marker.label, coord=marker.coord) | |
1479 | + | |
1480 | + # If the new marker has is_target=1 (True), we first create | |
1481 | + # a marker with is_target=0 (False), and then call __set_marker_as_target | |
1482 | + if marker.is_target: | |
1483 | + self.__set_marker_as_target(len(self.markers)-1) | |
1407 | 1484 | except: |
1408 | 1485 | wx.MessageBox(_("Invalid markers file."), _("InVesalius 3")) |
1409 | 1486 | |
... | ... | @@ -1430,82 +1507,51 @@ class MarkersPanel(wx.Panel): |
1430 | 1507 | style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT, |
1431 | 1508 | default_filename=default_filename) |
1432 | 1509 | |
1433 | - header_titles = ['x', 'y', 'z', 'alpha', 'beta', 'gamma', | |
1434 | - 'x_world', 'y_world', 'z_world', 'alpha_world', 'beta_world', 'gamma_world', | |
1435 | - 'r', 'g', 'b', 'size', 'marker_id', 'x_seed', 'y_seed', 'z_seed', 'target_id'] | |
1436 | - | |
1437 | - if filename: | |
1438 | - if self.list_coord: | |
1439 | - with open(filename, 'w', newline='') as file: | |
1440 | - file.writelines(['%s%i\n' % (const.MARKER_FILE_MAGICK_STRING, const.CURRENT_MARKER_FILE_VERSION)]) | |
1441 | - writer = csv.writer(file, dialect='markers_dialect') | |
1442 | - writer.writerow(header_titles) | |
1510 | + if not filename: | |
1511 | + return | |
1443 | 1512 | |
1444 | - writer.writerows(self.list_coord) | |
1513 | + try: | |
1514 | + with open(filename, 'w', newline='') as file: | |
1515 | + file.writelines(['%s%i\n' % (const.MARKER_FILE_MAGICK_STRING, const.CURRENT_MARKER_FILE_VERSION)]) | |
1516 | + writer = csv.writer(file, dialect='markers_dialect') | |
1517 | + writer.writerow(self.Marker.get_headers()) | |
1518 | + writer.writerows(marker.get_values() for marker in self.markers) | |
1519 | + file.close() | |
1520 | + except: | |
1521 | + wx.MessageBox(_("Error writing markers file."), _("InVesalius 3")) | |
1445 | 1522 | |
1446 | 1523 | def OnSelectColour(self, evt, ctrl): |
1447 | - self.marker_colour = [colour/255.0 for colour in ctrl.GetValue()] | |
1524 | + #TODO: Make sure GetValue returns 3 numbers (without alpha) | |
1525 | + self.marker_colour = [colour/255.0 for colour in ctrl.GetValue()][:3] | |
1448 | 1526 | |
1449 | 1527 | def OnSelectSize(self, evt, ctrl): |
1450 | 1528 | self.marker_size = ctrl.GetValue() |
1451 | 1529 | |
1452 | - def CreateMarker(self, coord=None, colour=None, size=None, label='*', target_id='*', seed=None): | |
1453 | - coord = coord or self.current_coord | |
1454 | - colour = colour or self.marker_colour | |
1455 | - size = size or self.marker_size | |
1456 | - seed = seed or self.current_seed | |
1457 | - | |
1458 | - position_world, orientation_world = imagedata_utils.convert_invesalius_to_world( | |
1459 | - position=coord[:3], | |
1460 | - orientation=coord[3:], | |
1461 | - ) | |
1462 | - | |
1463 | - # TODO: Use matrix coordinates and not world coordinates as current method. | |
1464 | - # This makes easier for inter-software comprehension. | |
1465 | - | |
1466 | - Publisher.sendMessage('Add marker', ball_id=len(self.list_coord), size=size, colour=colour, coord=coord[0:3]) | |
1467 | - | |
1468 | - # List of lists with coordinates and properties of a marker | |
1469 | - line = [] | |
1470 | - line.extend(coord) | |
1471 | - line.extend(position_world) | |
1472 | - line.extend(orientation_world) | |
1473 | - line.extend(colour) | |
1474 | - line.append(size) | |
1475 | - line.append(label) | |
1476 | - line.extend(seed) | |
1477 | - line.append(target_id) | |
1478 | - | |
1479 | - # Adding current line to a list of all markers already created | |
1480 | - if not self.list_coord: | |
1481 | - self.list_coord = [line] | |
1482 | - else: | |
1483 | - self.list_coord.append(line) | |
1530 | + def CreateMarker(self, coord=None, colour=None, size=None, label='*', is_target=0, seed=None): | |
1531 | + new_marker = self.Marker() | |
1532 | + new_marker.coord = coord or self.current_coord | |
1533 | + new_marker.colour = colour or self.marker_colour | |
1534 | + new_marker.size = size or self.marker_size | |
1535 | + new_marker.label = label | |
1536 | + new_marker.is_target = is_target | |
1537 | + new_marker.seed = seed or self.current_seed | |
1538 | + | |
1539 | + # Note that ball_id is zero-based, so we assign it len(self.markers) before the new marker is added | |
1540 | + Publisher.sendMessage('Add marker', ball_id=len(self.markers), | |
1541 | + size=new_marker.size, | |
1542 | + colour=new_marker.colour, | |
1543 | + coord=new_marker.coord[:3]) | |
1544 | + self.markers.append(new_marker) | |
1484 | 1545 | |
1485 | 1546 | # Add item to list control in panel |
1486 | 1547 | num_items = self.lc.GetItemCount() |
1487 | 1548 | self.lc.InsertItem(num_items, str(num_items + 1)) |
1488 | - self.lc.SetItem(num_items, 1, str(round(coord[0], 2))) | |
1489 | - self.lc.SetItem(num_items, 2, str(round(coord[1], 2))) | |
1490 | - self.lc.SetItem(num_items, 3, str(round(coord[2], 2))) | |
1491 | - self.lc.SetItem(num_items, 4, str(label)) | |
1549 | + self.lc.SetItem(num_items, 1, str(round(new_marker.x, 2))) | |
1550 | + self.lc.SetItem(num_items, 2, str(round(new_marker.y, 2))) | |
1551 | + self.lc.SetItem(num_items, 3, str(round(new_marker.z, 2))) | |
1552 | + self.lc.SetItem(num_items, 4, str(new_marker.label)) | |
1492 | 1553 | self.lc.EnsureVisible(num_items) |
1493 | 1554 | |
1494 | - def __getSelectedItems(self): | |
1495 | - """ | |
1496 | - Returns a (possibly empty) list of the selected items in the list control. | |
1497 | - """ | |
1498 | - selection = [] | |
1499 | - | |
1500 | - next = self.lc.GetFirstSelected() | |
1501 | - | |
1502 | - while next != -1: | |
1503 | - selection.append(next) | |
1504 | - next = self.lc.GetNextSelected(next) | |
1505 | - | |
1506 | - return selection | |
1507 | - | |
1508 | - | |
1509 | 1555 | class DbsPanel(wx.Panel): |
1510 | 1556 | def __init__(self, parent): |
1511 | 1557 | wx.Panel.__init__(self, parent) | ... | ... |