nettracer3d 0.9.7__py3-none-any.whl → 0.9.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of nettracer3d might be problematic. Click here for more details.

@@ -466,6 +466,102 @@ class ImageViewerWindow(QMainWindow):
466
466
  self.hold_update = False
467
467
  self._first_pan_done = False
468
468
 
469
+
470
+ def load_file(self):
471
+ """Load CSV or Excel file and convert to dictionary format."""
472
+ try:
473
+ # Open file dialog
474
+ file_filter = "Spreadsheet Files (*.csv *.xlsx);;CSV Files (*.csv);;Excel Files (*.xlsx)"
475
+ filename, _ = QFileDialog.getOpenFileName(
476
+ self,
477
+ "Load File",
478
+ "",
479
+ file_filter
480
+ )
481
+
482
+ if not filename:
483
+ return
484
+
485
+ # Read the file
486
+ if filename.endswith('.csv'):
487
+ df = pd.read_csv(filename)
488
+ elif filename.endswith('.xlsx'):
489
+ df = pd.read_excel(filename)
490
+ else:
491
+ QMessageBox.warning(self, "Error", "Please select a CSV or Excel file.")
492
+ return
493
+
494
+ if df.empty:
495
+ QMessageBox.warning(self, "Error", "The file appears to be empty.")
496
+ return
497
+
498
+ # Extract headers
499
+ headers = df.columns.tolist()
500
+ if len(headers) < 1:
501
+ QMessageBox.warning(self, "Error", "File must have at least 1 column.")
502
+ return
503
+
504
+ # Extract filename without extension for title
505
+ import os
506
+ title = os.path.splitext(os.path.basename(filename))[0]
507
+
508
+ if len(headers) == 1:
509
+ # Single column: pass header to metric, column data as list to data, nothing to value
510
+ metric = headers[0]
511
+ data = df.iloc[:, 0].tolist() # First column as list
512
+ value = None
513
+
514
+ self.format_for_upperright_table(
515
+ data=data,
516
+ metric=metric,
517
+ value=value,
518
+ title=title
519
+ )
520
+ else:
521
+ # Multiple columns: create dictionary as before
522
+ # First column header (for metric parameter)
523
+ metric = headers[0]
524
+
525
+ # Remaining headers (for value parameter)
526
+ value = headers[1:]
527
+
528
+ # Create dictionary
529
+ data_dict = {}
530
+
531
+ for index, row in df.iterrows():
532
+ key = row.iloc[0] # First column value as key
533
+
534
+ if len(headers) == 2:
535
+ # If only 2 columns, store single value
536
+ data_dict[key] = row.iloc[1]
537
+ else:
538
+ # If more than 2 columns, store as list
539
+ data_dict[key] = row.iloc[1:].tolist()
540
+
541
+ if len(value) == 1:
542
+ value = value[0]
543
+
544
+ # Call the parent method
545
+ self.format_for_upperright_table(
546
+ data=data_dict,
547
+ metric=metric,
548
+ value=value,
549
+ title=title
550
+ )
551
+
552
+ QMessageBox.information(
553
+ self,
554
+ "Success",
555
+ f"File '{title}' loaded successfully with {len(df)} entries."
556
+ )
557
+
558
+ except Exception as e:
559
+ QMessageBox.critical(
560
+ self,
561
+ "Error",
562
+ f"Failed to load file: {str(e)}"
563
+ )
564
+
469
565
  def popup_canvas(self):
470
566
  """Pop the canvas out into its own window"""
471
567
  if hasattr(self, 'popup_window') and self.popup_window.isVisible():
@@ -2494,6 +2590,12 @@ class ImageViewerWindow(QMainWindow):
2494
2590
 
2495
2591
  self.update_display(preserve_zoom=(current_xlim, current_ylim))
2496
2592
 
2593
+ if self.pan_mode:
2594
+ self.create_pan_background()
2595
+ current_xlim = self.ax.get_xlim()
2596
+ current_ylim = self.ax.get_ylim()
2597
+ self.update_display_pan_mode(current_xlim, current_ylim)
2598
+
2497
2599
 
2498
2600
  def toggle_zoom_mode(self):
2499
2601
  """Toggle zoom mode on/off."""
@@ -2501,7 +2603,9 @@ class ImageViewerWindow(QMainWindow):
2501
2603
 
2502
2604
  if self.zoom_mode:
2503
2605
  if self.pan_mode:
2504
- self.pan_button.click()
2606
+ self.update_display(preserve_zoom=(self.ax.get_xlim(), self.ax.get_ylim()))
2607
+ self.pan_mode = False
2608
+ self.pan_button.setChecked(False)
2505
2609
 
2506
2610
  self.pen_button.setChecked(False)
2507
2611
  self.brush_mode = False
@@ -2522,11 +2626,6 @@ class ImageViewerWindow(QMainWindow):
2522
2626
  current_xlim = self.ax.get_xlim()
2523
2627
  current_ylim = self.ax.get_ylim()
2524
2628
  self.update_display(preserve_zoom=(current_xlim, current_ylim))
2525
- if self.pan_mode:
2526
- current_xlim = self.ax.get_xlim()
2527
- current_ylim = self.ax.get_ylim()
2528
- self.update_display(preserve_zoom=(current_xlim, current_ylim))
2529
- self.pan_mode = False
2530
2629
 
2531
2630
  else:
2532
2631
  if self.machine_window is None:
@@ -2605,7 +2704,9 @@ class ImageViewerWindow(QMainWindow):
2605
2704
  if self.brush_mode:
2606
2705
 
2607
2706
  if self.pan_mode:
2608
- self.pan_button.click()
2707
+ self.update_display(preserve_zoom=(self.ax.get_xlim(), self.ax.get_ylim()))
2708
+ self.pan_mode = False
2709
+ self.pan_button.setChecked(False)
2609
2710
 
2610
2711
  self.pm = painting.PaintManager(parent = self)
2611
2712
 
@@ -3594,8 +3695,8 @@ class ImageViewerWindow(QMainWindow):
3594
3695
  # Multi-color highlight for machine window
3595
3696
  mask_1 = (highlight_data == 1)
3596
3697
  mask_2 = (highlight_data == 2)
3597
- rgba[mask_1] = [1, 1, 0, 0.5] # Yellow for 1
3598
- rgba[mask_2] = [0, 0.7, 1, 0.5] # Blue for 2
3698
+ rgba[mask_1] = [1, 1, 0, 0.3] # Yellow for 1
3699
+ rgba[mask_2] = [0, 0.7, 1, 0.3] # Blue for 2
3599
3700
 
3600
3701
  return rgba
3601
3702
 
@@ -4428,6 +4529,12 @@ class ImageViewerWindow(QMainWindow):
4428
4529
  cam_button.setStyleSheet("font-size: 24px;")
4429
4530
  cam_button.clicked.connect(self.snap)
4430
4531
  corner_layout.addWidget(cam_button)
4532
+
4533
+ load_button = QPushButton("📁")
4534
+ load_button.setFixedSize(40, 40)
4535
+ load_button.setStyleSheet("font-size: 24px;")
4536
+ load_button.clicked.connect(self.load_file)
4537
+ corner_layout.addWidget(load_button)
4431
4538
 
4432
4539
  # Set as corner widget
4433
4540
  menubar.setCornerWidget(corner_widget, Qt.Corner.TopRightCorner)
@@ -6088,17 +6195,6 @@ class ImageViewerWindow(QMainWindow):
6088
6195
  pass
6089
6196
  self.static_background = None
6090
6197
 
6091
- # Your existing machine_window logic
6092
- if self.machine_window is None:
6093
- try:
6094
- self.channel_data[4][self.current_slice, :, :] = n3d.overlay_arrays_simple(
6095
- self.channel_data[self.temp_chan][self.current_slice, :, :],
6096
- self.channel_data[4][self.current_slice, :, :])
6097
- self.load_channel(self.temp_chan, self.channel_data[4], data=True, end_paint=True)
6098
- self.channel_data[4] = None
6099
- self.channel_visible[4] = False
6100
- except:
6101
- pass
6102
6198
 
6103
6199
  # Get dimensions
6104
6200
  active_channels = [i for i in range(4) if self.channel_data[i] is not None]
@@ -6661,15 +6757,19 @@ class CustomTableView(QTableView):
6661
6757
  desc_action.triggered.connect(lambda checked, c=col: self.sort_table(c, ascending=False))
6662
6758
 
6663
6759
  # Different menus for top and bottom tables
6664
- if self in self.parent.data_table: # Top table
6760
+ if self.is_top_table: # Use the flag instead of checking membership
6665
6761
  save_menu = context_menu.addMenu("Save As")
6666
6762
  save_csv = save_menu.addAction("CSV")
6667
6763
  save_excel = save_menu.addAction("Excel")
6764
+
6765
+ if self.model() and len(self.model()._data.columns) == 2:
6766
+ thresh_action = context_menu.addAction("Use to Threshold Nodes")
6767
+ thresh_action.triggered.connect(lambda: self.thresh(self.create_threshold_dict()))
6768
+
6668
6769
  close_action = context_menu.addAction("Close All")
6669
-
6670
6770
  close_action.triggered.connect(self.close_all)
6671
6771
 
6672
- # Connect the actions
6772
+ # Connect the save actions
6673
6773
  save_csv.triggered.connect(lambda: self.save_table_as('csv'))
6674
6774
  save_excel.triggered.connect(lambda: self.save_table_as('xlsx'))
6675
6775
  else: # Bottom tables
@@ -6713,6 +6813,38 @@ class CustomTableView(QTableView):
6713
6813
  cursor_pos = QCursor.pos()
6714
6814
  context_menu.exec(cursor_pos)
6715
6815
 
6816
+
6817
+
6818
+ def thresh(self, special_dict):
6819
+ try:
6820
+ self.parent.special_dict = special_dict
6821
+ thresh_window = ThresholdWindow(self.parent, 4)
6822
+ thresh_window.show()
6823
+ except:
6824
+ pass
6825
+
6826
+ def create_threshold_dict(self):
6827
+ try:
6828
+ """Create a dictionary from the 2-column table data."""
6829
+ if not self.model() or not hasattr(self.model(), '_data'):
6830
+ return {}
6831
+
6832
+ df = self.model()._data
6833
+ if len(df.columns) != 2:
6834
+ return {}
6835
+
6836
+ # Create dictionary: {column_0_value: column_1_value}
6837
+ threshold_dict = {}
6838
+ for index, row in df.iterrows():
6839
+ key = row.iloc[0] # Column 0 value
6840
+ value = row.iloc[1] # Column 1 value
6841
+ threshold_dict[int(key)] = float(value)
6842
+
6843
+ return threshold_dict
6844
+ except:
6845
+ pass
6846
+
6847
+
6716
6848
  def sort_table(self, column, ascending=True):
6717
6849
  """Sort the table by the specified column."""
6718
6850
  try:
@@ -10991,6 +11123,8 @@ class MachineWindow(QMainWindow):
10991
11123
  def toggle_brush_mode(self):
10992
11124
  """Toggle brush mode on/off"""
10993
11125
  self.parent().brush_mode = self.brush_button.isChecked()
11126
+ if self.parent().pan_mode:
11127
+ self.parent().update_display(preserve_zoom=(self.parent().ax.get_xlim(), self.parent().ax.get_ylim()))
10994
11128
 
10995
11129
  if self.parent().brush_mode:
10996
11130
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nettracer3d
3
- Version: 0.9.7
3
+ Version: 0.9.9
4
4
  Summary: Scripts for intializing and analyzing networks from segmentations of three dimensional images.
5
5
  Author-email: Liam McLaughlin <liamm@wustl.edu>
6
6
  Project-URL: Documentation, https://nettracer3d.readthedocs.io/en/latest/
@@ -110,7 +110,7 @@ McLaughlin, L., Zhang, B., Sharma, S. et al. Three dimensional multiscalar neuro
110
110
 
111
111
  NetTracer3D was developed by Liam McLaughlin while working under Dr. Sanjay Jain at Washington University School of Medicine.
112
112
 
113
- -- Version 0.9.7 Updates --
113
+ -- Version 0.9.9 Updates --
114
114
 
115
- * Fixed a bug with the segmenter and color images.
116
- * The overlay 2 is now cyan by default.
115
+ * Tables can now be opened to the rightside upper widget if they are the right format.
116
+ * Similarly, tables that have the format node id column:numerical values can now be used liberally to threshold the nodes, meaning most outputs of network analysis can be used to threshold nodes.
@@ -6,7 +6,7 @@ nettracer3d/modularity.py,sha256=pborVcDBvICB2-g8lNoSVZbIReIBlfeBmjFbPYmtq7Y,224
6
6
  nettracer3d/morphology.py,sha256=jyDjYzrZ4LvI5jOyw8DLsxmo-i5lpqHsejYpW7Tq7Mo,19786
7
7
  nettracer3d/neighborhoods.py,sha256=iIaHU1COIdRtzRpAuIQKfLGLNKYFK3dL8Vb_EeJIlEA,46459
8
8
  nettracer3d/nettracer.py,sha256=d9Mhz-pHox5OfGGUSTrkTBJzCWCrbQvClTu2ANt-jUE,267943
9
- nettracer3d/nettracer_gui.py,sha256=VsZep2sgES10ZePVJDoUoFGgsg3wGRW-rAp1NMgQQwQ,628409
9
+ nettracer3d/nettracer_gui.py,sha256=ckTjhIutYwnh7qwvgCmcKs2DCCODKICsiCxd1sD77gA,633247
10
10
  nettracer3d/network_analysis.py,sha256=kBzsVaq4dZkMe0k-VGvQIUvM-tK0ZZ8bvb-wtsugZRQ,46150
11
11
  nettracer3d/network_draw.py,sha256=F7fw6Pcf4qWOhdKwLmhwqWdschbDlHzwCVolQC9imeU,14117
12
12
  nettracer3d/node_draw.py,sha256=kZcR1PekLg0riioNeGcALIXQyZ5PtHA_9MT6z7Zovdk,10401
@@ -17,9 +17,9 @@ nettracer3d/segmenter.py,sha256=aOO3PwZ2UeizEIFX7N8midwzTzbEDzgM2jQ7WTdbrUg,7067
17
17
  nettracer3d/segmenter_GPU.py,sha256=OUekQljLKPiC4d4hNZmqrRa9HSVQ6HcCnILiAfHE5Hg,78051
18
18
  nettracer3d/simple_network.py,sha256=dkG4jpc4zzdeuoaQobgGfL3PNo6N8dGKQ5hEEubFIvA,9947
19
19
  nettracer3d/smart_dilate.py,sha256=TvRUh6B4q4zIdCO1BWH-xgTdND5OUNmo99eyxG9oIAU,27145
20
- nettracer3d-0.9.7.dist-info/licenses/LICENSE,sha256=jnNT-yBeIAKAHpYthPvLeqCzJ6nSurgnKmloVnfsjCI,764
21
- nettracer3d-0.9.7.dist-info/METADATA,sha256=oJJZS4YPNc21NZd5sFUY6doxe5vpgzZwdEObUGIwswg,7067
22
- nettracer3d-0.9.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
- nettracer3d-0.9.7.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
24
- nettracer3d-0.9.7.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
25
- nettracer3d-0.9.7.dist-info/RECORD,,
20
+ nettracer3d-0.9.9.dist-info/licenses/LICENSE,sha256=jnNT-yBeIAKAHpYthPvLeqCzJ6nSurgnKmloVnfsjCI,764
21
+ nettracer3d-0.9.9.dist-info/METADATA,sha256=0T7rx2BBeAwH0NxEczmM0TeOaL6y0VmHMJlIDKkPu9g,7259
22
+ nettracer3d-0.9.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
23
+ nettracer3d-0.9.9.dist-info/entry_points.txt,sha256=Nx1rr_0QhJXDBHAQg2vcqCzLMKBzSHfwy3xwGkueVyc,53
24
+ nettracer3d-0.9.9.dist-info/top_level.txt,sha256=zsYy9rZwirfCEOubolhee4TyzqBAL5gSUeFMzhFTX8c,12
25
+ nettracer3d-0.9.9.dist-info/RECORD,,