pyTRACTnmr 0.1.1b1__py3-none-any.whl → 0.1.2b2__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.
pyTRACTnmr/window.py CHANGED
@@ -21,17 +21,15 @@ from PySide6.QtWidgets import (
21
21
  QHeaderView,
22
22
  QMenu,
23
23
  QSlider,
24
+ QComboBox,
24
25
  )
25
26
  from PySide6.QtGui import QAction
26
27
  from PySide6.QtCore import Qt, QPoint
27
28
 
28
29
  from matplotlib.widgets import SpanSelector
29
- try:
30
- from .widgets import MplCanvas, CustomNavigationToolbar
31
- from . import processing
32
- except ImportError:
33
- from widgets import MplCanvas, CustomNavigationToolbar
34
- import processing
30
+
31
+ from widgets import MplCanvas, CustomNavigationToolbar # type: ignore
32
+ import processing # type: ignore
35
33
 
36
34
 
37
35
  class TractApp(QMainWindow):
@@ -41,13 +39,11 @@ class TractApp(QMainWindow):
41
39
  self.resize(1200, 800)
42
40
 
43
41
  # Data State
44
- self.dic = None
45
- self.data = None
46
- self.proc_data = None
47
- self.time_points = None
48
42
  self.datasets: List[Dict[str, Any]] = []
49
43
  self.current_idx: int = -1
50
44
  self.selector: Optional[SpanSelector] = None
45
+ self.baseline_nodes: List[float] = []
46
+ self.picking_baseline: bool = False
51
47
 
52
48
  self.init_ui()
53
49
 
@@ -70,7 +66,17 @@ class TractApp(QMainWindow):
70
66
  self.table_data = QTableWidget()
71
67
  self.table_data.setColumnCount(9)
72
68
  self.table_data.setHorizontalHeaderLabels(
73
- ["Experiment", "Temperature (K)", "Delays", "Ra (Hz)", "Rb (Hz)", "Tau_C (ns)", "Err Ra", "Err Rb", "Err Tau_C"]
69
+ [
70
+ "Experiment",
71
+ "Temperature (K)",
72
+ "Delays",
73
+ "Ra (Hz)",
74
+ "Rb (Hz)",
75
+ "Tau_C (ns)",
76
+ "Err Ra",
77
+ "Err Rb",
78
+ "Err Tau_C",
79
+ ]
74
80
  )
75
81
  self.table_data.horizontalHeader().setSectionResizeMode(
76
82
  QHeaderView.ResizeMode.Stretch
@@ -101,6 +107,7 @@ class TractApp(QMainWindow):
101
107
  # Top: Spectrum
102
108
  self.canvas_spec = MplCanvas(self)
103
109
  self.toolbar_spec = CustomNavigationToolbar(self.canvas_spec, self)
110
+ self.canvas_spec.mpl_connect("button_press_event", self.on_canvas_click)
104
111
  widget_spec = QWidget()
105
112
  layout_spec = QVBoxLayout()
106
113
  lbl_spec = QLabel("<b>Processed Spectrum (Phase Check)</b>")
@@ -161,6 +168,14 @@ class TractApp(QMainWindow):
161
168
  self.input_points = QLineEdit("2048")
162
169
  self.input_points.editingFinished.connect(self.process_data)
163
170
 
171
+ self.combo_apod = QComboBox()
172
+ self.combo_apod.addItems(["Sine Bell (sp)", "Exponential (em)"])
173
+ self.combo_apod.currentIndexChanged.connect(self.update_apod_ui)
174
+ self.combo_apod.currentIndexChanged.connect(self.process_data)
175
+
176
+ self.input_lb = QLineEdit("5.0")
177
+ self.input_lb.editingFinished.connect(self.process_data)
178
+
164
179
  self.input_off = QLineEdit("0.35")
165
180
  self.input_off.editingFinished.connect(self.process_data)
166
181
 
@@ -186,15 +201,31 @@ class TractApp(QMainWindow):
186
201
  self.create_slider_layout(self.slider_p1_fine, self.input_p1),
187
202
  )
188
203
  layout_t1.addRow(QLabel("<b>Apodization & ZF</b>"))
204
+ layout_t1.addRow("Function:", self.combo_apod)
189
205
  layout_t1.addRow("Points (ZF):", self.input_points)
206
+ layout_t1.addRow("Line Broadening (Hz):", self.input_lb)
190
207
  layout_t1.addRow("Sine Offset:", self.input_off)
191
208
  layout_t1.addRow("Sine End:", self.input_end)
192
209
  layout_t1.addRow("Sine Power:", self.input_pow)
193
210
  layout_t1.addRow(QLabel("<b>Integration Range</b>"))
194
211
  layout_t1.addRow("Start (ppm):", self.input_int_start)
195
212
  layout_t1.addRow("End (ppm):", self.input_int_end)
213
+
214
+ layout_t1.addRow(QLabel("<b>Baseline Correction</b>"))
215
+ self.btn_pick_bl = QPushButton("Pick Nodes")
216
+ self.btn_pick_bl.setCheckable(True)
217
+ self.btn_pick_bl.clicked.connect(self.toggle_picking)
218
+ self.btn_clear_bl = QPushButton("Clear Nodes")
219
+ self.btn_clear_bl.clicked.connect(self.clear_baseline)
220
+ layout_bl = QHBoxLayout()
221
+ layout_bl.addWidget(self.btn_pick_bl)
222
+ layout_bl.addWidget(self.btn_clear_bl)
223
+ layout_t1.addRow(layout_bl)
224
+
196
225
  tab1.setLayout(layout_t1)
197
226
 
227
+ self.update_apod_ui()
228
+
198
229
  # Tab 2: Fitting
199
230
  tab2 = QWidget()
200
231
  layout_t2 = QFormLayout()
@@ -235,6 +266,30 @@ class TractApp(QMainWindow):
235
266
  layout.setContentsMargins(0, 0, 0, 0)
236
267
  return widget
237
268
 
269
+ def update_apod_ui(self) -> None:
270
+ is_sp = self.combo_apod.currentText().startswith("Sine")
271
+ layout = self.input_off.parent().layout()
272
+ if isinstance(layout, QFormLayout):
273
+ layout.setRowVisible(self.input_lb, not is_sp)
274
+ layout.setRowVisible(self.input_off, is_sp)
275
+ layout.setRowVisible(self.input_end, is_sp)
276
+ layout.setRowVisible(self.input_pow, is_sp)
277
+
278
+ def toggle_picking(self, checked: bool) -> None:
279
+ self.picking_baseline = checked
280
+ self.process_data()
281
+
282
+ def clear_baseline(self) -> None:
283
+ self.baseline_nodes = []
284
+ self.process_data()
285
+
286
+ def on_canvas_click(self, event) -> None:
287
+ if not self.picking_baseline or event.inaxes != self.canvas_spec.axes:
288
+ return
289
+ if event.button == 1 and event.xdata is not None:
290
+ self.baseline_nodes.append(event.xdata)
291
+ self.process_data()
292
+
238
293
  def update_phase_from_text(self) -> None:
239
294
  try:
240
295
  p0 = float(self.input_p0.text())
@@ -266,8 +321,13 @@ class TractApp(QMainWindow):
266
321
  try:
267
322
  delay_list = None
268
323
  if not os.path.exists(os.path.join(folder, "vdlist")):
324
+ QMessageBox.information(
325
+ self,
326
+ "Delay List Missing",
327
+ "The standard 'vdlist' file was not found. Please select a delay list file manually.",
328
+ )
269
329
  delay_list, _ = QFileDialog.getOpenFileName(
270
- self, "vdlist not found. Select delay list file:", folder
330
+ self, "Select delay list file", folder
271
331
  )
272
332
 
273
333
  tb = processing.TractBruker(folder, delay_list=delay_list)
@@ -289,6 +349,11 @@ class TractApp(QMainWindow):
289
349
  if self.current_idx < 0:
290
350
  return
291
351
  try:
352
+ # Capture current zoom level
353
+ xlim = self.canvas_spec.axes.get_xlim()
354
+ ylim = self.canvas_spec.axes.get_ylim()
355
+ has_zoom = len(self.canvas_spec.axes.lines) > 0
356
+
292
357
  p0 = self.slider_p0_coarse.value() + (self.slider_p0_fine.value() / 10.0)
293
358
  p1 = self.slider_p1_coarse.value() + (self.slider_p1_fine.value() / 10.0)
294
359
 
@@ -296,6 +361,11 @@ class TractApp(QMainWindow):
296
361
  self.input_p1.setText(f"{p1:.1f}")
297
362
 
298
363
  points = int(self.input_points.text())
364
+ apod_func = (
365
+ "sp" if self.combo_apod.currentText().startswith("Sine") else "em"
366
+ )
367
+ lb = float(self.input_lb.text())
368
+
299
369
  off = float(self.input_off.text())
300
370
  end = float(self.input_end.text())
301
371
  pow_val = float(self.input_pow.text())
@@ -305,8 +375,24 @@ class TractApp(QMainWindow):
305
375
  self.datasets[self.current_idx]["p1"] = p1
306
376
 
307
377
  tb = self.datasets[self.current_idx]["handler"]
378
+
379
+ nodes_idx = []
380
+ if self.baseline_nodes and tb.unit_converter:
381
+ for ppm in self.baseline_nodes:
382
+ idx = tb.unit_converter(ppm, "ppm")
383
+ nodes_idx.append(int(idx))
384
+ nodes_idx.sort()
385
+
308
386
  trace = tb.process_first_trace(
309
- p0, p1, points=points, off=off, end=end, pow=pow_val
387
+ p0,
388
+ p1,
389
+ points=points,
390
+ apod_func=apod_func,
391
+ lb=lb,
392
+ off=off,
393
+ end=end,
394
+ pow=pow_val,
395
+ nodes=nodes_idx,
310
396
  )
311
397
 
312
398
  self.canvas_spec.axes.clear()
@@ -320,21 +406,35 @@ class TractApp(QMainWindow):
320
406
  self.canvas_spec.axes.plot(trace, label="First Plane")
321
407
  self.canvas_spec.axes.legend()
322
408
 
323
- self.selector = SpanSelector(
324
- self.canvas_spec.axes,
325
- self.on_span_select,
326
- "horizontal",
327
- useblit=True,
328
- props=dict(alpha=0.2, facecolor="green"),
329
- interactive=True,
330
- drag_from_anywhere=True,
331
- )
332
- try:
333
- s = float(self.input_int_start.text())
334
- e = float(self.input_int_end.text())
335
- self.selector.extents = (min(s, e), max(s, e))
336
- except ValueError:
337
- pass
409
+ for node in self.baseline_nodes:
410
+ self.canvas_spec.axes.axvline(
411
+ x=node, color="r", linestyle="--", alpha=0.5
412
+ )
413
+
414
+ if has_zoom:
415
+ self.canvas_spec.axes.set_xlim(xlim)
416
+ self.canvas_spec.axes.set_ylim(ylim)
417
+
418
+ if not self.picking_baseline:
419
+ self.selector = SpanSelector(
420
+ self.canvas_spec.axes,
421
+ self.on_span_select,
422
+ "horizontal",
423
+ useblit=True,
424
+ props=dict(alpha=0.2, facecolor="green"),
425
+ interactive=True,
426
+ drag_from_anywhere=True,
427
+ )
428
+ else:
429
+ self.selector = None
430
+
431
+ if self.selector:
432
+ try:
433
+ s = float(self.input_int_start.text())
434
+ e = float(self.input_int_end.text())
435
+ self.selector.extents = (min(s, e), max(s, e))
436
+ except ValueError:
437
+ pass
338
438
 
339
439
  self.canvas_spec.draw()
340
440
  except Exception as e:
@@ -349,6 +449,11 @@ class TractApp(QMainWindow):
349
449
  p1 = self.slider_p1_coarse.value() + (self.slider_p1_fine.value() / 10.0)
350
450
 
351
451
  points = int(self.input_points.text())
452
+ apod_func = (
453
+ "sp" if self.combo_apod.currentText().startswith("Sine") else "em"
454
+ )
455
+ lb = float(self.input_lb.text())
456
+
352
457
  off = float(self.input_off.text())
353
458
  end_param = float(self.input_end.text())
354
459
  pow_val = float(self.input_pow.text())
@@ -369,24 +474,52 @@ class TractApp(QMainWindow):
369
474
  n_boot = 1000
370
475
  self.input_bootstraps.setText("1000")
371
476
 
372
- tb.split_process(p0, p1, points=points, off=off, end=end_param, pow=pow_val)
477
+ nodes_idx = []
478
+ if self.baseline_nodes and tb.unit_converter:
479
+ for ppm in self.baseline_nodes:
480
+ idx = tb.unit_converter(ppm, "ppm")
481
+ nodes_idx.append(int(idx))
482
+ nodes_idx.sort()
483
+
484
+ tb.split_process(
485
+ p0,
486
+ p1,
487
+ points=points,
488
+ apod_func=apod_func,
489
+ lb=lb,
490
+ off=off,
491
+ end=end_param,
492
+ pow=pow_val,
493
+ nodes=nodes_idx,
494
+ )
373
495
  tb.integrate_ppm(start_ppm, end_ppm)
374
496
  tb.calc_relaxation()
375
497
 
376
498
  b0 = float(self.input_field.text()) if self.input_field.text() else None
377
499
  tb.calc_tc(B0=b0, S2=s2_val, n_bootstrap=n_boot)
378
500
 
379
- x, y_a, y_b, popt_a, popt_b = tb.get_fit_data()
501
+ x, y_a, y_b, popt_a, popt_b, pcov_a, pcov_b = tb.get_fit_data()
380
502
 
381
503
  self.canvas_fit.axes.clear()
382
504
  self.canvas_fit.axes.plot(x, y_a, "bo", label=r"$\alpha -spin\ state$")
383
505
  self.canvas_fit.axes.plot(x, y_b, "ro", label=r"$\beta -spin\ state$")
384
- self.canvas_fit.axes.plot(
385
- x, processing.TractBruker._relax(x, *popt_a), "b-"
506
+
507
+ # Smooth lines for fit and CI
508
+ x_smooth = np.linspace(0, np.max(x) * 1.1, 100)
509
+ fit_a = processing.TractBruker._relax(x_smooth, *popt_a)
510
+ ci_a = tb.calc_confidence_interval(x_smooth, popt_a, pcov_a)
511
+ fit_b = processing.TractBruker._relax(x_smooth, *popt_b)
512
+ ci_b = tb.calc_confidence_interval(x_smooth, popt_b, pcov_b)
513
+
514
+ self.canvas_fit.axes.plot(x_smooth, fit_a, "b-")
515
+ self.canvas_fit.axes.fill_between(
516
+ x_smooth, fit_a - ci_a, fit_a + ci_a, color="b", alpha=0.2
386
517
  )
387
- self.canvas_fit.axes.plot(
388
- x, processing.TractBruker._relax(x, *popt_b), "r-"
518
+ self.canvas_fit.axes.plot(x_smooth, fit_b, "r-")
519
+ self.canvas_fit.axes.fill_between(
520
+ x_smooth, fit_b - ci_b, fit_b + ci_b, color="r", alpha=0.2
389
521
  )
522
+
390
523
  self.canvas_fit.axes.set_xlabel("Delay (s)")
391
524
  self.canvas_fit.axes.set_ylabel(r"$I/I_0$")
392
525
 
@@ -409,7 +542,7 @@ class TractApp(QMainWindow):
409
542
  if not path:
410
543
  return
411
544
  try:
412
- with open(path, 'w', newline='') as f:
545
+ with open(path, "w", newline="") as f:
413
546
  writer = csv.writer(f)
414
547
  headers = []
415
548
  for col in range(self.table_data.columnCount()):
@@ -417,7 +550,12 @@ class TractApp(QMainWindow):
417
550
  headers.append(item.text() if item else "")
418
551
  writer.writerow(headers)
419
552
  for row in range(self.table_data.rowCount()):
420
- row_data = [self.table_data.item(row, col).text() if self.table_data.item(row, col) else "" for col in range(self.table_data.columnCount())]
553
+ row_data = [
554
+ self.table_data.item(row, col).text()
555
+ if self.table_data.item(row, col)
556
+ else ""
557
+ for col in range(self.table_data.columnCount())
558
+ ]
421
559
  writer.writerow(row_data)
422
560
  except Exception as e:
423
561
  QMessageBox.critical(self, "Export Error", str(e))
@@ -434,7 +572,7 @@ class TractApp(QMainWindow):
434
572
  # Temperature
435
573
  try:
436
574
  temp = ds["handler"].attributes["acqus"]["TE"]
437
- except (KeyError, TypeError):
575
+ except KeyError, TypeError:
438
576
  temp = "N/A"
439
577
  item_temp = QTableWidgetItem(str(temp))
440
578
  item_temp.setFlags(item_temp.flags() & ~Qt.ItemFlag.ItemIsEditable)
@@ -503,7 +641,7 @@ class TractApp(QMainWindow):
503
641
  # Update Field Strength from parameters
504
642
  try:
505
643
  self.input_field.setText("{:.2f}".format(tb.attributes["acqus"]["SFO1"]))
506
- except (KeyError, AttributeError):
644
+ except KeyError, AttributeError:
507
645
  pass
508
646
 
509
647
  self.slider_p0_coarse.blockSignals(True)
@@ -526,6 +664,11 @@ class TractApp(QMainWindow):
526
664
  self.slider_p1_coarse.blockSignals(False)
527
665
  self.slider_p1_fine.blockSignals(False)
528
666
 
667
+ self.baseline_nodes = []
668
+ self.picking_baseline = False
669
+ self.btn_pick_bl.setChecked(False)
670
+
671
+ self.canvas_spec.axes.clear()
529
672
  self.process_data()
530
673
 
531
674
  # Update fit display
@@ -534,14 +677,23 @@ class TractApp(QMainWindow):
534
677
 
535
678
  if hasattr(tb, "Ra") and hasattr(tb, "popt_alpha"):
536
679
  try:
537
- x, y_a, y_b, popt_a, popt_b = tb.get_fit_data()
680
+ x, y_a, y_b, popt_a, popt_b, pcov_a, pcov_b = tb.get_fit_data()
538
681
  self.canvas_fit.axes.plot(x, y_a, "bo", label="Alpha (Anti-TROSY)")
539
682
  self.canvas_fit.axes.plot(x, y_b, "ro", label="Beta (TROSY)")
540
- self.canvas_fit.axes.plot(
541
- x, processing.TractBruker._relax(x, *popt_a), "b-"
683
+
684
+ x_smooth = np.linspace(0, np.max(x) * 1.1, 100)
685
+ fit_a = processing.TractBruker._relax(x_smooth, *popt_a)
686
+ ci_a = tb.calc_confidence_interval(x_smooth, popt_a, pcov_a)
687
+ fit_b = processing.TractBruker._relax(x_smooth, *popt_b)
688
+ ci_b = tb.calc_confidence_interval(x_smooth, popt_b, pcov_b)
689
+
690
+ self.canvas_fit.axes.plot(x_smooth, fit_a, "b-")
691
+ self.canvas_fit.axes.fill_between(
692
+ x_smooth, fit_a - ci_a, fit_a + ci_a, color="b", alpha=0.2
542
693
  )
543
- self.canvas_fit.axes.plot(
544
- x, processing.TractBruker._relax(x, *popt_b), "r-"
694
+ self.canvas_fit.axes.plot(x_smooth, fit_b, "r-")
695
+ self.canvas_fit.axes.fill_between(
696
+ x_smooth, fit_b - ci_b, fit_b + ci_b, color="r", alpha=0.2
545
697
  )
546
698
 
547
699
  tau_c_val = getattr(tb, "tau_c", 0.0)
@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyTRACTnmr
3
+ Version: 0.1.2b2
4
+ Summary: A simple gui based application to process and analyse TRACT data from NMR spectroscopy.
5
+ Project-URL: Homepage, https://github.com/debadutta-patra/pyTRACTnmr
6
+ Project-URL: Repository, https://github.com/debadutta-patra/pyTRACTnmr
7
+ Project-URL: Issues, https://github.com/debadutta-patra/pyTRACTnmr/issues
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.14
10
+ Requires-Dist: matplotlib>=3.10.8
11
+ Requires-Dist: nmrglue>=0.11
12
+ Requires-Dist: numpy>=2.4.2
13
+ Requires-Dist: pyside6-stubs>=6.7.3.0
14
+ Requires-Dist: pyside6>=6.10.2
15
+ Requires-Dist: scipy-stubs>=1.17.0.2
16
+ Requires-Dist: scipy>=1.17.0
17
+ Description-Content-Type: text/markdown
18
+
19
+ # pyTRACTnmr
20
+
21
+ **pyTRACTnmr** is a graphical user interface (GUI) application designed for the processing and analysis of TRACT (TROSY for Rotational Correlation Times) experiments in NMR spectroscopy. It provides a streamlined workflow to go from raw Bruker data to calculated rotational correlation times ($\tau_c$) with robust error estimation. Currently this only supports data collected with Bruker spectrometers using pulseprogram `tractf3gpphwg`.
22
+
23
+ ## Features
24
+
25
+ - **User-Friendly Interface**: Built with PySide6 for a responsive and native experience.
26
+ - **Bruker Data Import**: Directly load Bruker experiment directories.
27
+ - **Interactive Spectral Processing**:
28
+ - Real-time 0th and 1st order phase correction.
29
+ - Adjustable Apodization (Sine-bell squared) and Zero Filling.
30
+ - **Interactive Region Selection**: Drag directly on the spectrum to define integration limits.
31
+ - **Advanced Analysis**:
32
+ - Automatic calculation of relaxation rates ($R_\alpha$ and $R_\beta$).
33
+ - Determination of Rotational Correlation Time ($\tau_c$).
34
+ - **Bootstrap Error Analysis**: rigorous uncertainty estimation for $\tau_c$.
35
+ - **Data Management**:
36
+ - Tabular view of multiple loaded experiments.
37
+ - Context menu to export results to CSV.
38
+
39
+ ## Installation
40
+
41
+ ### Prerequisites
42
+
43
+ * Python 3.14 or higher recommended (Though it is not tested it should run with earlier version of Python 3).
44
+ * [uv](https://github.com/astral-sh/uv) (optional, but recommended for building).
45
+
46
+ ### Installation
47
+ #### Quick Start with uv
48
+ The fastest way to try pyTRACTnmr without installation is:
49
+
50
+ ```bash
51
+ uvx pytractnmr
52
+ ```
53
+
54
+ #### Using pip
55
+
56
+ ```bash
57
+ pip install pyTRACTnmr
58
+ ```
59
+
60
+ #### From Source
61
+
62
+ 1. **Clone the repository:**
63
+ ```bash
64
+ git clone https://github.com/debadutta-patra/pyTRACTnmr.git
65
+ cd pyTRACTnmr
66
+ ```
67
+
68
+ 2. **Install the package:**
69
+
70
+ Using `uv` (fastest):
71
+ ```bash
72
+ uv pip install .
73
+ ```
74
+
75
+ Using standard `pip`:
76
+ ```bash
77
+ pip install .
78
+ ```
79
+
80
+ ## Usage
81
+
82
+ ### Launching the App
83
+
84
+ After installation, you can start the application from the terminal:
85
+
86
+ ```bash
87
+ pytractnmr
88
+ ```
89
+
90
+ Or run it as a python module:
91
+
92
+ ```bash
93
+ python -m pyTRACTnmr.main
94
+ ```
95
+
96
+ ### Analysis Workflow
97
+
98
+ 1. **Load Data**: Click **"Load Bruker Directory"** and select your experiment folder.
99
+ 2. **Process**:
100
+ * Use the **Processing** tab to adjust phase correction sliders.
101
+ * Drag the **green selection box** on the top spectrum plot to define the integration region (Start/End ppm).
102
+ 3. **Fit**:
103
+ * Switch to the **Fitting** tab.
104
+ * Input experimental parameters (Field Strength, CSA, etc.).
105
+ * Set the number of **Bootstraps** (e.g., 1000).
106
+ * Click **"Calculate Tau_c"**.
107
+ 4. **Export**:
108
+ * Right-click the results table to **Export Table to CSV**.
109
+
110
+ ## Dependencies
111
+
112
+ * `PySide6`
113
+ * `numpy`
114
+ * `scipy`
115
+ * `matplotlib`
116
+ * `nmrglue`
117
+
118
+ ## License
119
+
120
+ This project is licensed under the GNU General Public License v3.0 License - see the LICENSE file for details.
@@ -0,0 +1,10 @@
1
+ pyTRACTnmr/__init__.py,sha256=5rxO0E3s6JQ3X_bBwWIR3tOVEZrS4ZIC-_LZPemSnmQ,24
2
+ pyTRACTnmr/main.py,sha256=YPoQdIN0VIOknnAgWAHBk1ZdefID4NFa8jFY8piOZEM,253
3
+ pyTRACTnmr/processing.py,sha256=kH9tW5JhtdIYdoADGhLACX8BNGv4M5H_QX1O73z1I-Q,15466
4
+ pyTRACTnmr/widgets.py,sha256=DlSSWUZ_soVfeXvkm5s26zPKoxREUW-4x5__vvXtpj4,2818
5
+ pyTRACTnmr/window.py,sha256=9pu6LNJ4mskJYnHWmlk_NdHAAavK84jlqm0WGchNYe8,30061
6
+ pytractnmr-0.1.2b2.dist-info/METADATA,sha256=XTtlQlPSvPzDQpeTwVH-zkVSt5SMLmtmzWMsRomb9YI,3661
7
+ pytractnmr-0.1.2b2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ pytractnmr-0.1.2b2.dist-info/entry_points.txt,sha256=nub_92b4LtA_hmf3wD1ZQv6rbjBX24fHI0DtbZg5A1M,52
9
+ pytractnmr-0.1.2b2.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
10
+ pytractnmr-0.1.2b2.dist-info/RECORD,,
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pyTRACTnmr = pyTRACTnmr.main:main