Skip to content

Commit ca8c9d9

Browse files
committed
Fix conflicts
2 parents 2e6b5f1 + 17dab94 commit ca8c9d9

File tree

3 files changed

+163
-155
lines changed

3 files changed

+163
-155
lines changed

spikeinterface_gui/backend_panel.py

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55

66
from .viewlist import possible_class_views
77
from .layout_presets import get_layout_description
8-
from .utils_global import get_size_bottom_row, get_size_top_row
9-
8+
from .utils_global import fill_unnecessary_space, get_present_zones_in_half_of_layout
109
# Used by views to emit/trigger signals
1110
class SignalNotifier(param.Parameterized):
1211
spike_selection_changed = param.Event()
@@ -283,26 +282,64 @@ def create_main_layout(self):
283282
allow_drag=False,
284283
)
285284

286-
gs = self.make_half_layout(gs, ['zone1', 'zone2', 'zone5', 'zone6'], layout_zone, 0)
287-
gs = self.make_half_layout(gs, ['zone3', 'zone4', 'zone7', 'zone8'], layout_zone, 2)
285+
gs = self.make_half_layout(gs, layout_zone, "left")
286+
gs = self.make_half_layout(gs, layout_zone, "right")
288287

289288
self.main_layout = gs
290289

291-
def make_half_layout(self, gs, all_zones, layout_zone, shift):
292-
293-
is_zone = [(layout_zone.get(zone) is not None) and (len(layout_zone.get(zone)) > 0) for zone in all_zones]
294-
is_zone_array = np.reshape(is_zone, (2,2))
295-
original_zone_array = copy(is_zone_array)
296-
297-
for zone_index, zone_name in enumerate(all_zones):
298-
row = zone_index // 2
299-
col = zone_index % 2
300-
if row == 0:
301-
num_rows, num_cols = get_size_top_row(row, col, is_zone_array, original_zone_array)
302-
elif row == 1:
303-
num_rows, num_cols = get_size_bottom_row(row, col, is_zone_array, original_zone_array)
304-
if num_rows > 0 and num_cols > 0:
305-
gs[slice(row, row + num_rows), slice(col+shift,col+num_cols+shift)] = layout_zone.get(zone_name)
290+
def make_half_layout(self, gs, layout_zone, left_or_right):
291+
"""
292+
Function contains the logic for the greedy layout. Given the 2x2 box of zones
293+
294+
1 2 3 4
295+
5 6 or 7 8
296+
297+
Then depending on which zones are non-zero, a different layout is generated using splits.
298+
299+
The zone indices in the second box (34,78) are equal to the zone indices first box (12,56)
300+
shifted by 2. We take advantage of this fact.
301+
"""
302+
303+
shift = 0 if left_or_right == "left" else 2
304+
305+
layout_zone = fill_unnecessary_space(layout_zone, shift)
306+
present_zones = get_present_zones_in_half_of_layout(layout_zone, shift)
307+
308+
# `fill_unnecessary_space` ensures that zone{1+shift} always exists
309+
if present_zones == set([f'zone{1+shift}']):
310+
gs[0,0] = layout_zone.get(f'zone{1+shift}')
311+
312+
# Layouts with two non-zero zones
313+
if present_zones == set([f'zone{1+shift}', f'zone{2+shift}']):
314+
gs[slice(0, 1), slice(0+shift,1+shift)] = layout_zone.get(f'zone{1+shift}')
315+
gs[slice(0, 1), slice(1+shift,2+shift)] = layout_zone.get(f'zone{2+shift}')
316+
elif present_zones == set([f'zone{1+shift}', f'zone{5+shift}']):
317+
gs[slice(0, 1), slice(0+shift,2+shift)] = layout_zone.get(f'zone{1+shift}')
318+
gs[slice(1, 2), slice(0+shift,2+shift)] = layout_zone.get(f'zone{5+shift}')
319+
elif present_zones == set([f'zone{1+shift}', f'zone{6+shift}']):
320+
gs[slice(0, 1), slice(0+shift,1+shift)] = layout_zone.get(f'zone{1+shift}')
321+
gs[slice(0, 1), slice(1+shift,2+shift)] = layout_zone.get(f'zone{6+shift}')
322+
323+
# Layouts with three non-zero zones
324+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{5+shift}']):
325+
gs[slice(0, 1), slice(0+shift,1+shift)] = layout_zone.get(f'zone{1+shift}')
326+
gs[slice(0, 2), slice(1+shift,2+shift)] = layout_zone.get(f'zone{2+shift}')
327+
gs[slice(1, 2), slice(0+shift,1+shift)] = layout_zone.get(f'zone{5+shift}')
328+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{6+shift}']):
329+
gs[slice(0, 2), slice(0+shift,1+shift)] = layout_zone.get(f'zone{1+shift}')
330+
gs[slice(0, 1), slice(1+shift,2+shift)] = layout_zone.get(f'zone{2+shift}')
331+
gs[slice(1, 2), slice(1+shift,1+shift)] = layout_zone.get(f'zone{6+shift}')
332+
elif present_zones == set([f'zone{1+shift}', f'zone{5+shift}', f'zone{6+shift}']):
333+
gs[slice(0, 1), slice(0+shift,2+shift)] = layout_zone.get(f'zone{1+shift}')
334+
gs[slice(1, 2), slice(0+shift,1+shift)] = layout_zone.get(f'zone{5+shift}')
335+
gs[slice(1, 2), slice(1+shift,2+shift)] = layout_zone.get(f'zone{6+shift}')
336+
337+
# Layouts with four non-zero zones
338+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{5+shift}', f'zone{6+shift}']):
339+
gs[slice(0, 1), slice(0+shift,1+shift)] = layout_zone.get(f'zone{1+shift}')
340+
gs[slice(0, 1), slice(1+shift,2+shift)] = layout_zone.get(f'zone{2+shift}')
341+
gs[slice(1, 2), slice(0+shift,1+shift)] = layout_zone.get(f'zone{5+shift}')
342+
gs[slice(1, 2), slice(1+shift,2+shift)] = layout_zone.get(f'zone{6+shift}')
306343

307344
return gs
308345

spikeinterface_gui/backend_qt.py

Lines changed: 61 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from .viewlist import possible_class_views
1010
from .layout_presets import get_layout_description
11-
from .utils_global import get_size_bottom_row, get_size_top_row
11+
from .utils_global import fill_unnecessary_space, get_present_zones_in_half_of_layout
1212

1313
from .utils_qt import qt_style, add_stretch_to_qtoolbar
1414

@@ -200,9 +200,9 @@ def create_main_layout(self):
200200
view_names = [view_name for view_name in view_names if view_name in self.views.keys()]
201201
widgets_zone[zone] = view_names
202202

203-
self.make_dock(widgets_zone, ['zone1', 'zone2', 'zone5', 'zone6'], "left", col_shift=0)
204-
self.make_dock(widgets_zone, ['zone3', 'zone4', 'zone7', 'zone8'], "right", col_shift=2)
205-
203+
self.make_half_layout(widgets_zone, "left")
204+
self.make_half_layout(widgets_zone, "right")
205+
206206
# make tabs
207207
for zone, view_names in widgets_zone.items():
208208
n = len(widgets_zone[zone])
@@ -217,103 +217,70 @@ def create_main_layout(self):
217217
# make visible the first of each zone
218218
self.docks[view_name0].raise_()
219219

220-
def make_dock(self, widgets_zone, all_zones, side_of_window, col_shift):
221-
222-
all_zones_array = np.transpose(np.reshape(all_zones, (2,2)))
223-
is_zone = np.array([(widgets_zone.get(zone) is not None) and (len(widgets_zone.get(zone)) > 0) for zone in all_zones])
224-
is_zone_array = np.reshape(is_zone, (2,2))
225-
226-
# If the first non-zero zero (from left to right) is on the bottom, move it up
227-
for column_index, zones_in_columns in enumerate(is_zone_array):
228-
if np.any(zones_in_columns):
229-
first_is_top = zones_in_columns[0]
230-
if not first_is_top:
231-
top_zone = f"zone{column_index+1+col_shift}"
232-
bottom_zone = f"zone{column_index+5+col_shift}"
233-
widgets_zone[top_zone] = widgets_zone[bottom_zone]
234-
widgets_zone[bottom_zone] = []
235-
continue
236-
237-
is_zone = np.array([(widgets_zone.get(zone) is not None) and (len(widgets_zone.get(zone)) > 0) for zone in all_zones])
238-
is_zone_array = np.reshape(is_zone, (2,2))
239-
original_zone_array = copy(is_zone_array)
240-
241-
# First we split horizontally any columns which are two rows long.
242-
# For later, group the zones between these splits
243-
all_groups = []
244-
group = []
245-
for col_index, zones in enumerate(all_zones_array):
246-
col = col_index % 2
247-
is_a_zone = original_zone_array[:,col]
248-
num_row_0, _ = get_size_top_row(0, col, is_zone_array, original_zone_array)
249-
# this function affects is_zone_array so must be run
250-
_, _ = get_size_bottom_row(1, col, is_zone_array, original_zone_array)
251-
252-
if num_row_0 == 2:
253-
if len(group) > 0:
254-
all_groups.append(group)
255-
group = []
256-
allowed_zones = zones[is_a_zone]
257-
all_groups.append(allowed_zones)
258-
else:
259-
for zone in zones[is_a_zone]:
260-
group.append(zone)
261-
262-
if len(group) > 0:
263-
all_groups.append(group)
264-
265-
if len(all_groups) == 0:
266-
return
267-
268-
first_zone = all_groups[0][0]
269-
first_dock = widgets_zone[first_zone][0]
270-
dock = self.docks[first_dock]
271-
self.addDockWidget(areas[side_of_window], dock)
272-
273-
for group in reversed(all_groups[1:]):
274-
digits = np.array([int(s[-1]) for s in group])
275-
sorted_indices = np.argsort(digits)
276-
sorted_arr = np.array(group)[sorted_indices]
277-
view_name = widgets_zone[sorted_arr[0]][0]
278-
dock = self.docks[view_name]
279-
self.splitDockWidget(self.docks[first_dock], dock, orientations['horizontal'])
280-
281-
# Now take each sub-group, and split vertically if appropriate
282-
new_all_groups = []
283-
for group in all_groups:
284-
285-
if len(group) == 1:
286-
# if only one in group, not need to split
287-
continue
288-
289-
top_zones = [zone for zone in group if zone in ['zone1', 'zone2', 'zone3', 'zone4']]
290-
bottom_zones = [zone for zone in group if zone in ['zone5', 'zone6', 'zone7', 'zone8']]
291-
new_all_groups.append([top_zones, bottom_zones])
220+
def make_half_layout(self, widgets_zone, left_or_right):
221+
"""
222+
Function contains the logic for the greedy layout. Given the 2x2 box of zones
292223
293-
if len(top_zones) > 0 and len(bottom_zones) > 0:
224+
1 2 3 4
225+
5 6 or 7 8
294226
295-
top_view_name = widgets_zone[top_zones[0]][0]
296-
top_dock = self.docks[top_view_name]
227+
Then depending on which zones are non-zero, a different layout is generated using splits.
297228
298-
bottom_view_name = widgets_zone[bottom_zones[0]][0]
299-
bottom_dock = self.docks[bottom_view_name]
229+
The zone indices in the second box (34,78) are equal to the zone indices first box (12,56)
230+
shifted by 2. We take advantage of this fact.
231+
"""
300232

301-
self.splitDockWidget(top_dock, bottom_dock, orientations['vertical'])
233+
shift = 0 if left_or_right == "left" else 2
302234

303-
# Finally, split all the sub-sub-groups horizontally
304-
for top_bottom_groups in new_all_groups:
305-
for group in top_bottom_groups:
306-
307-
if len(group) <= 1:
308-
# if only one in group, no need to split
309-
continue
310-
311-
first_zone_name = widgets_zone[group[0]][0]
312-
for zone in reversed(group[1:]):
313-
zone_name = widgets_zone[zone][0]
314-
self.splitDockWidget(self.docks[first_zone_name], self.docks[zone_name], orientations['horizontal'])
235+
widgets_zone = fill_unnecessary_space(widgets_zone, shift)
236+
present_zones = get_present_zones_in_half_of_layout(widgets_zone, shift)
315237

238+
if len(present_zones) == 0:
239+
return
316240

241+
# The movements from earlier guarantee that the top-left zone is non-zero. Make this the initial zone
242+
view_name = widgets_zone[f"zone{1+shift}"][0]
243+
dock = self.docks[view_name]
244+
self.addDockWidget(areas[left_or_right], dock)
245+
246+
# The main logic: apply splittings between different zones and in
247+
# different orders, depending on which zones are present.
248+
249+
# Layouts with two non-zero zones
250+
if present_zones == set([f'zone{1+shift}', f'zone{2+shift}']):
251+
self.make_split(1,2,"horizontal", widgets_zone, shift)
252+
elif present_zones == set([f'zone{1+shift}', f'zone{5+shift}']):
253+
self.make_split(1,5,"vertical", widgets_zone, shift)
254+
elif present_zones == set([f'zone{1+shift}', f'zone{6+shift}']):
255+
self.make_split(1,6,"horizontal", widgets_zone, shift)
256+
257+
# Layouts with three non-zero zones
258+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{5+shift}']):
259+
self.make_split(1,2,"horizontal", widgets_zone, shift)
260+
self.make_split(1,5,"vertical", widgets_zone, shift)
261+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{6+shift}']):
262+
self.make_split(1,2,"horizontal", widgets_zone, shift)
263+
self.make_split(2,6,"vertical", widgets_zone, shift)
264+
elif present_zones == set([f'zone{1+shift}', f'zone{5+shift}', f'zone{6+shift}']):
265+
self.make_split(1,5,"vertical", widgets_zone, shift)
266+
self.make_split(5,6,"horizontal", widgets_zone, shift)
267+
268+
# Layout with four non-zero zones
269+
elif present_zones == set([f'zone{1+shift}', f'zone{2+shift}', f'zone{5+shift}', f'zone{6+shift}']):
270+
self.make_split(1,5,"vertical", widgets_zone, shift)
271+
self.make_split(1,2,"horizontal", widgets_zone, shift)
272+
self.make_split(5,6,"horizontal", widgets_zone, shift)
273+
274+
275+
def make_split(self, zone_index_1, zone_index_2, orientation, widgets_zone, shift):
276+
"""
277+
Splits the zone at `zone_{zone_index_1+shift}` into two zones
278+
(`zone_{zone_index_1+shift}` and `zone_{zone_index_2+shift}`)
279+
with an `orientation` split.
280+
"""
281+
widget_1 = widgets_zone[f"zone{zone_index_1+shift}"][0]
282+
widget_2 = widgets_zone[f"zone{zone_index_2+shift}"][0]
283+
self.splitDockWidget(self.docks[widget_1], self.docks[widget_2], orientations[orientation])
317284

318285
# used by to tell the launcher this is closed
319286
def closeEvent(self, event):

spikeinterface_gui/utils_global.py

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,48 @@
11
import numpy as np
22

3-
def get_size_top_row(initial_row, initial_col, is_zone_array, original_zone_array):
4-
5-
if original_zone_array[initial_row][initial_col] == False:
6-
return 0,0
7-
8-
num_rows = is_zone_array[initial_row][initial_col]*1
9-
num_cols = num_rows
10-
11-
num_rows += (not is_zone_array[1][initial_col])*1
12-
13-
if num_rows == 1:
14-
for zone in is_zone_array[0,1+initial_col:]:
15-
if zone == True:
16-
break
17-
num_cols += 1
18-
elif num_rows == 2:
19-
for zone1, zone2 in np.transpose(is_zone_array[:,1+initial_col:]):
20-
if zone1 == True or zone2 == True:
21-
break
22-
num_cols += 1
23-
24-
is_zone_array[initial_row:initial_row+num_rows,initial_col:initial_col+num_cols] = True
25-
26-
return num_rows, num_cols
27-
28-
def get_size_bottom_row(initial_row, initial_col, is_zone_array, original_zone_array):
29-
30-
if original_zone_array[initial_row][initial_col] == False:
31-
return 0,0
32-
33-
num_rows = is_zone_array[initial_row][initial_col]*1
34-
if num_rows == 0:
35-
return 0, 0
36-
num_cols = num_rows
37-
38-
for zone in is_zone_array[1,1+initial_col:]:
39-
if zone == True:
40-
break
41-
else:
42-
num_cols += 1
43-
44-
return num_rows, num_cols
3+
# Functions for the layout
4+
5+
def fill_unnecessary_space(layout_zone, shift):
6+
"""
7+
Used when making layouts. In the zoning algorithm,
8+
certain layouts are equivalent to each other e.g.
9+
10+
zone1 zone2 . .
11+
. . is equivalent to zone5 zone6
12+
13+
and
14+
15+
. zone2 zone1 .
16+
. zone6 is equivalent to zone5 .
17+
18+
This function moves zones left-wards and upwards in a way that preserves
19+
the layouts and ensures that the top-left zone is non-zero.
20+
"""
21+
22+
# Move the right hand column leftwards if the left-hand column is missing
23+
if len(layout_zone[f'zone{1+shift}']) == 0 and len(layout_zone[f'zone{5+shift}']) == 0:
24+
layout_zone[f'zone{1+shift}'] = layout_zone[f'zone{2+shift}']
25+
layout_zone[f'zone{5+shift}'] = layout_zone[f'zone{6+shift}']
26+
layout_zone[f'zone{2+shift}'] = []
27+
layout_zone[f'zone{6+shift}'] = []
28+
29+
# Move the bottom-left zone to the top-left, if the top-left is missing
30+
# These steps reduce the number of layouts we have to consider
31+
if len(layout_zone[f'zone{1+shift}']) == 0:
32+
layout_zone[f'zone{1+shift}'] = layout_zone[f'zone{5+shift}']
33+
layout_zone[f'zone{5+shift}'] = []
34+
35+
return layout_zone
36+
37+
38+
def get_present_zones_in_half_of_layout(layout_zone, shift):
39+
"""
40+
Returns the zones which contain at least one view, for either:
41+
left-hand zones 1,2,5,6 (shift=0)
42+
right-hand zones 3,4,7,8 (shift=2)
43+
"""
44+
zones_in_half = [f'zone{1+shift}', f'zone{2+shift}', f'zone{5+shift}', f'zone{6+shift}']
45+
half_dict = {key: value for key, value in layout_zone.items() if key in zones_in_half}
46+
is_present = [views is not None and len(views) > 0 for views in half_dict.values()]
47+
present_zones = set(np.array(list(half_dict.keys()))[np.array(is_present)])
48+
return present_zones

0 commit comments

Comments
 (0)