• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

int-brain-lab / iblrig / 12279337432

11 Dec 2024 03:15PM UTC coverage: 47.031% (+0.2%) from 46.79%
12279337432

Pull #751

github

d4edef
web-flow
Merge eea51f2f7 into 2f9d65d86
Pull Request #751: Fiber trajectory GUI

0 of 114 new or added lines in 1 file covered. (0.0%)

1076 existing lines in 22 files now uncovered.

4246 of 9028 relevant lines covered (47.03%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/iblrig/gui/fiber_trajectory.py
1
# -------------------------------------------------------------------------------------------------
2
# Imports
3
# -------------------------------------------------------------------------------------------------
4

NEW
5
import json
×
NEW
6
import sys
×
7

NEW
8
import matplotlib.pyplot as plt
×
NEW
9
import numpy as np
×
NEW
10
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
×
NEW
11
from matplotlib.figure import Figure
×
NEW
12
from PyQt5.QtGui import QColor, QPalette
×
NEW
13
from PyQt5.QtWidgets import (
×
14
    QApplication,
15
    QFormLayout,
16
    QLabel,
17
    QLineEdit,
18
    QMainWindow,
19
    QVBoxLayout,
20
    QWidget,
21
)
22

NEW
23
from ibllib.atlas import AllenAtlas, Insertion
×
NEW
24
from ibllib.tests import TEST_DB
×
NEW
25
from one.webclient import AlyxClient
×
26

27
# -------------------------------------------------------------------------------------------------
28
# Global variables
29
# -------------------------------------------------------------------------------------------------
30

NEW
31
ACTUAL_DB = {
×
32
    'base_url': 'https://alyx.internationalbrainlab.org',
33
    'username': 'USERNAME',
34
    'password': 'PASSWORD',
35
}
36

37

38
# -------------------------------------------------------------------------------------------------
39
# Plotting functions
40
# -------------------------------------------------------------------------------------------------
41

42

NEW
43
def plot_trajectories(ax, names, trajectories, atlas=None):
×
NEW
44
    assert atlas
×
NEW
45
    top = atlas.top
×
NEW
46
    extent = np.hstack((atlas.bc.xlim, atlas.bc.ylim))
×
NEW
47
    ax.imshow(top, extent=extent, cmap='Greys_r')
×
NEW
48
    ax.set_xlim(atlas.bc.xlim)
×
NEW
49
    ax.set_ylim(atlas.bc.ylim)
×
50

NEW
51
    prop_cycle = plt.rcParams['axes.prop_cycle']
×
NEW
52
    colors = prop_cycle.by_key()['color']
×
NEW
53
    eps = 0.0001
×
NEW
54
    for name, traj, color in zip(names, trajectories, colors, strict=True):
×
NEW
55
        x = traj[0, 0]
×
NEW
56
        y = traj[0, 1]
×
NEW
57
        if x == y == 0:
×
NEW
58
            continue
×
NEW
59
        ax.plot(traj[:, 0], traj[:, 1])
×
NEW
60
        ax.plot([x], [y], 'o', color=color)
×
NEW
61
        ax.text(x - eps, y - 4 * eps, name, color=color)
×
62

63

64
# -------------------------------------------------------------------------------------------------
65
# Trajectory loader
66
# -------------------------------------------------------------------------------------------------
67

68

NEW
69
class TrajectoryLoader:
×
NEW
70
    def __init__(self, atlas=None):
×
NEW
71
        self.alyx = AlyxClient(**TEST_DB)
×
72
        # self.alyx = AlyxClient(**ACTUAL_DB)
NEW
73
        self.atlas = atlas
×
74

NEW
75
    def _save_rest(self, n, v='read', pk=None):
×
NEW
76
        d = self.alyx.rest(n, v, id=pk)
×
NEW
77
        with open(f'{n}.json', 'w') as f:
×
NEW
78
            json.dump(d, f, indent=1)
×
79

NEW
80
    def save_subject(self, pk):
×
NEW
81
        self._save_rest(self, 'subjects', pk=pk)
×
82

NEW
83
    def save_session(self, pk):
×
NEW
84
        self._save_rest(self, 'sessions', pk=pk)
×
85

NEW
86
    def save_insertion(self, pk):
×
NEW
87
        self._save_rest(self, 'insertions', pk=pk)
×
88

NEW
89
    def save_trajectories(self):
×
NEW
90
        self._save_rest(self, 'trajectories', v='list')
×
91

NEW
92
    def create(self, name, path):
×
NEW
93
        with open(path) as f:
×
NEW
94
            self.alyx.rest(name, 'create', data=json.load(f))
×
95

NEW
96
    def get_trajectory(self, chronic_insertion):
×
97
        # retrieve planned/micromanip (priority) trajectory of chronic insertion
NEW
98
        trajectories = self.alyx.rest('trajectories', 'list', chronic_insertion=chronic_insertion)
×
NEW
99
        if not trajectories:
×
NEW
100
            return
×
NEW
101
        priorities = {
×
102
            'Planned': 1,
103
            'Micro-manipulator': 2,
104
        }
NEW
105
        trajectory = sorted(trajectories, key=lambda t: priorities.get(t['provenance'], 0))[-1]
×
NEW
106
        ins = Insertion.from_dict(trajectory, brain_atlas=self.atlas)
×
NEW
107
        return np.vstack((ins.entry, ins.tip))
×
108

NEW
109
    def get_trajectories(self, subject):
×
NEW
110
        chronic_insertions = self.alyx.rest('chronic-insertions', 'list', subject=subject, model='fiber')
×
NEW
111
        names = [i['name'] for i in chronic_insertions]
×
NEW
112
        trajectories = [self.get_trajectory(i['id']) for i in chronic_insertions]
×
NEW
113
        return names, trajectories
×
114

115

116
# -------------------------------------------------------------------------------------------------
117
# GUI
118
# -------------------------------------------------------------------------------------------------
119

120

NEW
121
class MainWindow(QMainWindow):
×
NEW
122
    def __init__(self, nickname=None, names=None, trajectories=None):
×
NEW
123
        super().__init__()
×
124

NEW
125
        self.atlas = AllenAtlas(25)
×
NEW
126
        self.atlas.compute_surface()
×
127

NEW
128
        self.nickname = nickname
×
NEW
129
        self.names = names
×
NEW
130
        self.trajectories = trajectories
×
131

NEW
132
        self.setWindowTitle('Fiber insertions')
×
133

134
        # Main widget
NEW
135
        main_widget = QWidget()
×
NEW
136
        main_layout = QVBoxLayout()
×
137

138
        # Top panel
NEW
139
        top_panel = QWidget()
×
NEW
140
        top_layout = QVBoxLayout()
×
141

142
        # First row: Label
NEW
143
        label_subject = QLabel(self.nickname)
×
NEW
144
        top_layout.addWidget(label_subject)
×
145

146
        # Second row: Label and Textbox
NEW
147
        self.textboxes = []
×
NEW
148
        prop_cycle = plt.rcParams['axes.prop_cycle']
×
NEW
149
        colors = prop_cycle.by_key()['color']
×
NEW
150
        for i in range(len(self.trajectories)):
×
NEW
151
            color = colors[i]
×
NEW
152
            self.textboxes.append(QLineEdit())
×
NEW
153
            rl = QFormLayout()
×
NEW
154
            c = self.trajectories[i][0]  # 0 is entry point, 1 is tip
×
NEW
155
            s = f'{self.names[i]}: AP {c[0]:.4f}, ML {c[1]:.4f}, DV {c[2]:.4f}'
×
NEW
156
            label = QLabel(s)
×
NEW
157
            palette = label.palette()
×
NEW
158
            palette.setColor(QPalette.WindowText, QColor(color))
×
NEW
159
            label.setPalette(palette)
×
NEW
160
            rl.addRow(label, self.textboxes[i])
×
161

NEW
162
            top_layout.addLayout(rl)
×
163

NEW
164
        top_panel.setLayout(top_layout)
×
165

166
        # Bottom panel
NEW
167
        bottom_panel = QWidget()
×
NEW
168
        bottom_layout = QVBoxLayout()
×
169

170
        # Matplotlib figure
NEW
171
        self.figure = Figure()
×
NEW
172
        self.canvas = FigureCanvas(self.figure)
×
NEW
173
        self.ax = self.figure.add_subplot(111)
×
174

NEW
175
        bottom_layout.addWidget(self.canvas)
×
NEW
176
        bottom_panel.setLayout(bottom_layout)
×
177

178
        # Add panels to the main layout
NEW
179
        main_layout.addWidget(top_panel)
×
NEW
180
        main_layout.addWidget(bottom_panel)
×
181

NEW
182
        main_widget.setLayout(main_layout)
×
NEW
183
        self.setCentralWidget(main_widget)
×
184

NEW
185
        plot_trajectories(self.ax, self.names, self.trajectories, atlas=self.atlas)
×
186

187

NEW
188
if __name__ == '__main__':
×
NEW
189
    fig, ax = plt.subplots(1, 1)
×
190

191
    # subject = 'd69bacb2-5ac0-40ac-9be9-98f2fb97d858'
192
    # session = '66f6e1f0-a4a2-4a18-9588-38cf31377fd4'
193
    # probe_insertion = '59538275-27fd-4d56-9658-0c956b0e7c6f'
194
    # chronic_insertion = '0d5c77db-51b7-47f2-aef2-2655520731a0'
195
    # trajectory_estimate = 'f0925fd5-22b3-472d-b43a-d3bb91f33502'
196
    # nickname = 'KM_012'
197
    # birth_date = '2023-08-30'
198
    # lab = 'cortexlab'
199

200
    # Mock data
NEW
201
    nickname = 'CQ004'
×
NEW
202
    names = ['NBM', 'PPT']
×
203
    # NOTE: the unit should be meter, but the trajectory numbers below were given in millimeters
204
    # hence the `*1e-3`
NEW
205
    trajectories = [
×
206
        np.array([[-0.70, +1.75, -4.15], [+0.70, -1.75, +4.15]]) * 1e-3,
207
        np.array([[-4.72, -1.25, -2.75], [+4.72, +1.25, +2.75]]) * 1e-3,
208
    ]
209

NEW
210
    app = QApplication(sys.argv)
×
NEW
211
    window = MainWindow(nickname, names, trajectories)
×
NEW
212
    window.show()
×
NEW
213
    sys.exit(app.exec_())
×
214

215
    # from PyQt5 import QtWidgets
216
    # from iblrig.gui.wizard import RigWizard
217
    # app = QtWidgets.QApplication(['', '--no-sandbox'])
218
    # app.setStyle('Fusion')
219
    # w = RigWizard(alyx=alyx, test_subject_name='KM_012')
220
    # w.show()
221
    # app.exec()
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc