2
2
3
3
from collections import defaultdict
4
4
from collections .abc import Iterable , Mapping , Sequence
5
- from enum import Enum
6
5
from itertools import cycle
7
6
from typing import TYPE_CHECKING , Literal , cast
8
7
12
11
from superqt import QCollapsible , QElidingLabel , QIconifyIcon , ensure_main_thread
13
12
from superqt .utils import qthrottled , signals_blocked
14
13
14
+ from ndv .viewer ._components import (
15
+ ChannelMode ,
16
+ ChannelModeButton ,
17
+ DimToggleButton ,
18
+ QSpinner ,
19
+ )
20
+
15
21
from ._backends import get_canvas
16
22
from ._data_wrapper import DataWrapper
17
23
from ._dims_slider import DimsSliders
46
52
ALL_CHANNELS = slice (None )
47
53
48
54
49
- class ChannelMode (str , Enum ):
50
- COMPOSITE = "composite"
51
- MONO = "mono"
52
-
53
- def __str__ (self ) -> str :
54
- return self .value
55
-
56
-
57
- class ChannelModeButton (QPushButton ):
58
- def __init__ (self , parent : QWidget | None = None ):
59
- super ().__init__ (parent )
60
- self .setCheckable (True )
61
- self .toggled .connect (self .next_mode )
62
-
63
- # set minimum width to the width of the larger string 'composite'
64
- self .setMinimumWidth (92 ) # magic number :/
65
-
66
- def next_mode (self ) -> None :
67
- if self .isChecked ():
68
- self .setMode (ChannelMode .MONO )
69
- else :
70
- self .setMode (ChannelMode .COMPOSITE )
71
-
72
- def mode (self ) -> ChannelMode :
73
- return ChannelMode .MONO if self .isChecked () else ChannelMode .COMPOSITE
74
-
75
- def setMode (self , mode : ChannelMode ) -> None :
76
- # we show the name of the next mode, not the current one
77
- other = ChannelMode .COMPOSITE if mode is ChannelMode .MONO else ChannelMode .MONO
78
- self .setText (str (other ))
79
- self .setChecked (mode == ChannelMode .MONO )
80
-
81
-
82
- class DimToggleButton (QPushButton ):
83
- def __init__ (self , parent : QWidget | None = None ):
84
- icn = QIconifyIcon ("f7:view-2d" , color = "#333333" )
85
- icn .addKey ("f7:view-3d" , state = QIconifyIcon .State .On , color = "white" )
86
- super ().__init__ (icn , "" , parent )
87
- self .setCheckable (True )
88
- self .setChecked (True )
89
-
90
-
91
55
class NDViewer (QWidget ):
92
56
"""A viewer for ND arrays.
93
57
@@ -197,6 +161,8 @@ def __init__(
197
161
198
162
# place to display dataset summary
199
163
self ._data_info_label = QElidingLabel ("" , parent = self )
164
+ self ._progress_spinner = QSpinner (self )
165
+
200
166
# place to display arbitrary text
201
167
self ._hover_info_label = QLabel ("" , self )
202
168
# the canvas that displays the images
@@ -232,10 +198,16 @@ def __init__(
232
198
btns .addWidget (self ._ndims_btn )
233
199
btns .addWidget (self ._set_range_btn )
234
200
201
+ info = QHBoxLayout ()
202
+ info .setContentsMargins (0 , 0 , 0 , 2 )
203
+ info .setSpacing (0 )
204
+ info .addWidget (self ._data_info_label )
205
+ info .addWidget (self ._progress_spinner )
206
+
235
207
layout = QVBoxLayout (self )
236
208
layout .setSpacing (2 )
237
209
layout .setContentsMargins (6 , 6 , 6 , 6 )
238
- layout .addWidget ( self . _data_info_label )
210
+ layout .addLayout ( info )
239
211
layout .addWidget (self ._canvas .qwidget (), 1 )
240
212
layout .addWidget (self ._hover_info_label )
241
213
layout .addWidget (self ._dims_sliders )
@@ -432,6 +404,7 @@ def _update_data_for_index(self, index: Indices) -> None:
432
404
raise type (e )(f"Failed to index data with { index } : { e } " ) from e
433
405
434
406
f .add_done_callback (self ._on_data_slice_ready )
407
+ self ._progress_spinner .show ()
435
408
436
409
def closeEvent (self , a0 : QCloseEvent | None ) -> None :
437
410
if self ._last_future is not None :
@@ -451,7 +424,7 @@ def _on_data_slice_ready(
451
424
# because the future has a reference to this widget in its _done_callbacks
452
425
# which will prevent the widget from being garbage collected if the future
453
426
self ._last_future = None
454
-
427
+ self . _progress_spinner . hide ()
455
428
if future .cancelled ():
456
429
return
457
430
0 commit comments