66import weakref
77
88from .viewlist import possible_class_views
9- from .layout_presets import get_layout_description
9+ from .layout_presets import get_layout_description , get_size_bottom_row , get_size_top_row
1010
1111from .utils_qt import qt_style , add_stretch_to_qtoolbar
1212
13-
1413# Used by views to emit/trigger signals
1514class SignalNotifier (QT .QObject ):
1615 spike_selection_changed = QT .pyqtSignal ()
@@ -137,7 +136,7 @@ def listen_setting_changes(view):
137136class QtMainWindow (QT .QMainWindow ):
138137 main_window_closed = QT .pyqtSignal (object )
139138
140- def __init__ (self , controller , parent = None , layout_preset = None , layout = None ):
139+ def __init__ (self , controller , parent = None , layout_preset = None , layout = None , screenshot_name = "" ):
141140 QT .QMainWindow .__init__ (self , parent )
142141
143142 self .controller = controller
@@ -154,6 +153,15 @@ def __init__(self, controller, parent=None, layout_preset=None, layout=None):
154153 view ._refresh ()
155154 self .controller .signal_handler .activate ()
156155
156+ self .screenshot_name = screenshot_name
157+ #QT.QTimer.singleShot(200, self.saveScreenshot)
158+
159+ def saveScreenshot (self ):
160+
161+ screen = QT .QApplication .primaryScreen ()
162+ screenshot = screen .grabWindow ( self .winId () )
163+ screenshot .save (self .screenshot_name , 'jpg' )
164+
157165 # TODO sam : all veiws are always refreshed at the moment so this is useless.
158166 # uncommen this when ViewBase.is_view_visible() work correctly
159167 # for view_name, dock in self.docks.items():
@@ -186,6 +194,42 @@ def make_views(self):
186194
187195
188196 def create_main_layout (self ):
197+ """
198+ We rely on PyQt docks so that the user can adjust the size of each widget. These
199+ are made iteratively by splitting an existing dock. The algorithm to create the
200+ splits based on our layout dict is in three steps. It is complicated, so we provide
201+ an example. Let's "*" represent a zone with something in it and "o" a zone without.
202+ Consider the example
203+
204+ 1 2 3 4
205+ * * o *
206+ * o * *
207+
208+ First, we determine which columns should be two rows long. Then split the docks
209+ around these. In this case, only column 2 should be long. So the split is
210+
211+ 1 2 3 4
212+ * | * | o *
213+ * | o | * *
214+
215+ Next, for each sub-region, we split vertically. The second column cannot be split.
216+
217+ 1 2 3 4
218+ * | * | o *
219+ - - -
220+ * | o | * *
221+
222+ Finally, in each sub-sub-region, we check how many times we need to split horizontally.
223+ Here the lower 3/4 row can be split
224+
225+ 1 2 3 4
226+ * | * | o *
227+ - - -
228+ * | o | * | *
229+
230+ Each final region contains exactly one zone. All of our possible layouts can be split up
231+ in this way.
232+ """
189233 import warnings
190234
191235 warnings .filterwarnings ("ignore" , category = RuntimeWarning , module = "pyqtgraph" )
@@ -200,47 +244,157 @@ def create_main_layout(self):
200244 view_names = [view_name for view_name in view_names if view_name in self .views .keys ()]
201245 widgets_zone [zone ] = view_names
202246
203- ## Handle left
204- first_left = None
205- for zone in ['zone1' , 'zone5' , 'zone2' , 'zone6' ]:
206- if len (widgets_zone [zone ]) == 0 :
207- continue
208- view_name = widgets_zone [zone ][0 ]
247+ import numpy as np
248+ from copy import copy
249+
250+ all_zones = [f'zone{ a } ' for a in range (1 ,9 )]
251+ all_zones_array = np .transpose (np .reshape (all_zones , (2 ,4 )))
252+ is_zone = np .array ([(widgets_zone .get (zone ) is not None ) and (len (widgets_zone .get (zone )) > 0 ) for zone in all_zones ])
253+ is_zone_array = np .reshape (is_zone , (2 ,4 ))
254+ original_zone_array = copy (is_zone_array )
255+
256+ first_nonzero_column = np .arange (4 )[np .any (original_zone_array , axis = 0 )][0 ]
257+ first_is_top = original_zone_array [:,first_nonzero_column ][0 ]
258+ if not first_is_top :
259+ top_zone = f"zone{ first_nonzero_column + 1 } "
260+ bottom_zone = f"zone{ first_nonzero_column + 5 } "
261+ widgets_zone [top_zone ] = widgets_zone [bottom_zone ]
262+ widgets_zone [bottom_zone ] = []
263+
264+ is_zone = np .array ([(widgets_zone .get (zone ) is not None ) and (len (widgets_zone .get (zone )) > 0 ) for zone in all_zones ])
265+ is_zone_array = np .reshape (is_zone , (2 ,4 ))
266+ original_zone_array = copy (is_zone_array )
267+
268+ num_rows_1 , num_cols_1 = get_size_top_row (0 ,0 , is_zone_array , original_zone_array )
269+ num_rows_2 , num_cols_2 = get_size_top_row (0 ,1 , is_zone_array , original_zone_array )
270+ num_rows_3 , num_cols_3 = get_size_top_row (0 ,2 , is_zone_array , original_zone_array )
271+ num_rows_4 , num_cols_4 = get_size_top_row (0 ,3 , is_zone_array , original_zone_array )
272+
273+ num_rows_5 , num_cols_5 = get_size_bottom_row (1 ,0 , is_zone_array , original_zone_array )
274+ num_rows_6 , num_cols_6 = get_size_bottom_row (1 ,1 , is_zone_array , original_zone_array )
275+ num_rows_7 , num_cols_7 = get_size_bottom_row (1 ,2 , is_zone_array , original_zone_array )
276+ num_rows_8 , num_cols_8 = get_size_bottom_row (1 ,3 , is_zone_array , original_zone_array )
277+
278+ num_rows = np .array ([num_rows_1 , num_rows_2 , num_rows_3 , num_rows_4 , num_rows_5 , num_rows_6 , num_rows_7 , num_rows_8 ])
279+ num_cols = np .array ([num_cols_1 , num_cols_2 , num_cols_3 , num_cols_4 , num_cols_5 , num_cols_6 , num_cols_7 , num_cols_8 ])
280+
281+ num_rows_array = np .reshape (num_rows , (2 ,4 ))
282+ num_cols_array = np .reshape (num_cols , (2 ,4 ))
283+
284+ group = []
285+ all_groups = []
286+ for num_row , num_col , is_a_zone , zones in zip (np .transpose (num_rows_array ), np .transpose (num_cols_array ), np .transpose (original_zone_array ), all_zones_array ):
287+ if num_row [0 ] == 2 :
288+ if len (group ) > 0 :
289+ all_groups .append (group )
290+ group = []
291+ allowed_zones = zones [is_a_zone ]
292+ all_groups .append (allowed_zones )
293+ else :
294+ for zone in zones [is_a_zone ]:
295+ group .append (zone )
296+
297+ if len (group ) > 0 :
298+ all_groups .append (group )
299+
300+ first_dock = None
301+ print (f"{ all_groups } " )
302+ first_zone = all_groups [0 ][0 ]
303+ print (f"{ first_zone = } " )
304+ if first_zone in ['zone5' , 'zone6' , 'zone7' , 'zone8' ]:
305+ above_zone = f'zone{ int (first_zone [- 1 ])- 4 } '
306+ widgets_zone [above_zone ] = widgets_zone [first_zone ]
307+ first_zone = above_zone
308+ first_dock = widgets_zone [first_zone ][0 ]
309+ dock = self .docks [first_dock ]
310+ self .addDockWidget (areas ['left' ], dock )
311+
312+ for group in reversed (all_groups [1 :]):
313+ digits = np .array ([int (s [- 1 ]) for s in group ])
314+ sorted_indices = np .argsort (digits )
315+ sorted_arr = np .array (group )[sorted_indices ]
316+ view_name = widgets_zone [sorted_arr [0 ]][0 ]
209317 dock = self .docks [view_name ]
210- if len (widgets_zone [zone ]) > 0 and first_left is None :
211- self .addDockWidget (areas ['left' ], dock )
212- first_left = view_name
213- elif zone == 'zone5' :
214- self .splitDockWidget (self .docks [first_left ], dock , orientations ['vertical' ])
215- elif zone == 'zone2' :
216- self .splitDockWidget (self .docks [first_left ], dock , orientations ['horizontal' ])
217- elif zone == 'zone6' :
218- if len (widgets_zone ['zone5' ]) > 0 :
219- z = widgets_zone ['zone5' ][0 ]
220- self .splitDockWidget (self .docks [z ], dock , orientations ['horizontal' ])
221- else :
222- self .splitDockWidget (self .docks [first_left ], dock , orientations ['vertical' ])
318+ print (f"Splitting { first_dock } , { view_name } " )
319+ self .splitDockWidget (self .docks [first_dock ], dock , orientations ['horizontal' ])
223320
224- ## Handle right
225- first_top = None
226- for zone in ['zone3' , 'zone4' , 'zone7' , 'zone8' ]:
227- if len (widgets_zone [zone ]) == 0 :
321+ new_all_groups = []
322+ for group in all_groups :
323+ if len (group ) == 1 :
228324 continue
229- view_name = widgets_zone [zone ][0 ]
230- dock = self .docks [view_name ]
231- if len (widgets_zone [zone ]) > 0 and first_top is None :
232- self .addDockWidget (areas ['right' ], dock )
233- first_top = view_name
234- elif zone == 'zone4' :
235- self .splitDockWidget (self .docks [first_top ], dock , orientations ['horizontal' ])
236- elif zone == 'zone7' :
237- self .splitDockWidget (self .docks [first_top ], dock , orientations ['vertical' ])
238- elif zone == 'zone8' :
239- if len (widgets_zone ['zone4' ]) > 0 :
240- z = widgets_zone ['zone4' ][0 ]
241- self .splitDockWidget (self .docks [z ], dock , orientations ['vertical' ])
325+ else :
326+ top_zones = [zone for zone in group if zone in ['zone1' , 'zone2' , 'zone3' , 'zone4' ]]
327+ bottom_zones = [zone for zone in group if zone in ['zone5' , 'zone6' , 'zone7' , 'zone8' ]]
328+ new_all_groups .append ([top_zones , bottom_zones ])
329+
330+ for top_and_bottom_groups in new_all_groups :
331+
332+ top_groups , bottom_groups = top_and_bottom_groups
333+
334+ if len (top_groups ) > 0 and len (bottom_groups ) > 0 :
335+
336+ top_view_name = widgets_zone [top_groups [0 ]][0 ]
337+ top_dock = self .docks [top_view_name ]
338+
339+ bottom_view_name = widgets_zone [bottom_groups [0 ]][0 ]
340+ bottom_dock = self .docks [bottom_view_name ]
341+
342+ print (f"Splitting { top_view_name } , { bottom_view_name } " )
343+ self .splitDockWidget (top_dock , bottom_dock , orientations ['vertical' ])
344+
345+ for top_bottom_groups in new_all_groups :
346+ for group in top_bottom_groups :
347+ if len (group ) <= 1 :
348+ continue
242349 else :
243- self .splitDockWidget (self .docks [first_top ], dock , orientations ['horizontal' ])
350+ first_zone_name = widgets_zone [group [0 ]][0 ]
351+
352+ for zone in reversed (group [1 :]):
353+ zone_name = widgets_zone [zone ][0 ]
354+ print (f"Splitting { first_zone_name } , { zone_name } " )
355+ self .splitDockWidget (self .docks [first_zone_name ], self .docks [zone_name ], orientations ['horizontal' ])
356+
357+ # ## Handle left
358+ # first_left = None
359+ # for zone in ['zone1', 'zone5', 'zone2', 'zone6']:
360+ # if len(widgets_zone[zone]) == 0:
361+ # continue
362+ # view_name = widgets_zone[zone][0]
363+ # dock = self.docks[view_name]
364+ # if len(widgets_zone[zone]) > 0 and first_left is None:
365+ # self.addDockWidget(areas['left'], dock)
366+ # first_left = view_name
367+ # elif zone == 'zone5':
368+ # self.splitDockWidget(self.docks[first_left], dock, orientations['vertical'])
369+ # elif zone == 'zone2':
370+ # self.splitDockWidget(self.docks[first_left], dock, orientations['horizontal'])
371+ # elif zone == 'zone6':
372+ # if len(widgets_zone['zone5']) > 0:
373+ # z = widgets_zone['zone5'][0]
374+ # self.splitDockWidget(self.docks[z], dock, orientations['horizontal'])
375+ # else:
376+ # self.splitDockWidget(self.docks[first_left], dock, orientations['vertical'])
377+
378+ # ## Handle right
379+ # first_top = None
380+ # for zone in ['zone3', 'zone4', 'zone7', 'zone8']:
381+ # if len(widgets_zone[zone]) == 0:
382+ # continue
383+ # view_name = widgets_zone[zone][0]
384+ # dock = self.docks[view_name]
385+ # if len(widgets_zone[zone]) > 0 and first_top is None:
386+ # self.addDockWidget(areas['right'], dock)
387+ # first_top = view_name
388+ # elif zone == 'zone4':
389+ # self.splitDockWidget(self.docks[first_top], dock, orientations['horizontal'])
390+ # elif zone == 'zone7':
391+ # self.splitDockWidget(self.docks[first_top], dock, orientations['vertical'])
392+ # elif zone == 'zone8':
393+ # if len(widgets_zone['zone4']) > 0:
394+ # z = widgets_zone['zone4'][0]
395+ # self.splitDockWidget(self.docks[z], dock, orientations['vertical'])
396+ # else:
397+ # self.splitDockWidget(self.docks[first_top], dock, orientations['horizontal'])
244398
245399 # make tabs
246400 for zone , view_names in widgets_zone .items ():
0 commit comments