11import pyqtgraph .Qt as Qt
22import pyqtgraph as pg
3- import pyapr
43import matplotlib .pyplot as plt
54import numpy as np
65
76
8- class customSlider ():
9- def __init__ (self , window , label_name ):
7+ class DoubleSlider (Qt .QtWidgets .QSlider ):
8+ """
9+ Extends QSlider to allow floating-point values
10+
11+ Adapted from Stack Overflow answer https://stackoverflow.com/a/50300848
12+ by user bfris (https://stackoverflow.com/users/9705687/bfris)
13+ """
14+
15+ # create a signal that we can connect to if necessary
16+ doubleValueChanged = Qt .QtCore .pyqtSignal (float )
17+
18+ def __init__ (self , decimals = 2 , * args , ** kwargs ):
19+ super (DoubleSlider , self ).__init__ (* args , ** kwargs )
20+ self ._multi = 10 ** decimals
21+ self .valueChanged .connect (self .emitDoubleValueChanged )
22+
23+ def emitDoubleValueChanged (self ):
24+ value = float (super (DoubleSlider , self ).value ()) / self ._multi
25+ self .doubleValueChanged .emit (value )
26+
27+ def value (self ):
28+ return float (super (DoubleSlider , self ).value ()) / self ._multi
29+
30+ def setValue (self , value ):
31+ super (DoubleSlider , self ).setValue (int (value * self ._multi ))
32+
33+ def setMinimum (self , value ):
34+ return super (DoubleSlider , self ).setMinimum (value * self ._multi )
35+
36+ def setMaximum (self , value ):
37+ return super (DoubleSlider , self ).setMaximum (value * self ._multi )
38+
39+ def setSingleStep (self , value ):
40+ return super (DoubleSlider , self ).setSingleStep (value * self ._multi )
41+
42+ def singleStep (self ):
43+ return float (super (DoubleSlider , self ).singleStep ()) / self ._multi
44+
45+
46+ class CustomSlider :
47+ def __init__ (self , window , label_name , decimals = 0 ):
48+
49+ if decimals < 0 or not isinstance (decimals , int ):
50+ raise ValueError ('CustomSlider initialized with \' decimals\' ={}. Only non-negative integers are allowed.' .format (decimals ))
51+
52+ self .decimals = decimals
53+
54+ self .slider = DoubleSlider (decimals , Qt .QtCore .Qt .Horizontal , window )
55+ self .maxBox = Qt .QtWidgets .QDoubleSpinBox (window , decimals = self .decimals )
1056
11- self .slider = Qt .QtWidgets .QSlider (Qt .QtCore .Qt .Horizontal , window )
1257 self .label = Qt .QtWidgets .QLabel (window )
13- self .maxBox = Qt .QtWidgets .QSpinBox (window )
1458
1559 self .maxBox .setMaximum (64000 )
1660 self .maxBox .setValue (300 )
@@ -24,9 +68,9 @@ def __init__(self, window, label_name):
2468 self .slider .setValue (1 )
2569 self .slider .setMaximum (self .maxBox .value ())
2670
27- sz_label = 100
28- sz_slider = 200
29- sz_box = 75
71+ self . sz_label = 200
72+ self . sz_slider = 200
73+ self . sz_box = 90
3074
3175 def move (self , loc1 , loc2 ):
3276
@@ -39,21 +83,20 @@ def move(self, loc1, loc2):
3983 self .maxBox .setFixedWidth (self .sz_box )
4084
4185 def updateRange (self ):
42- max = self .maxBox .value ()
43- self .slider .setMaximum (max )
44- self .slider .setTickInterval (1 )
86+ max_val = self .maxBox .value ()
87+ self .slider .setMaximum (max_val )
4588
4689 def connectSlider (self , function ):
4790 self .slider .valueChanged .connect (function )
4891
4992 def updateText (self ):
50- text_str = self .label_name + ": " + str (self .slider .value ())
93+ val_str = '{:.{prec}f}' .format (self .slider .value (), prec = self .decimals )
94+ text_str = self .label_name + ': ' + val_str
5195 self .label .setText (text_str )
5296
5397
5498class MainWindowImage (Qt .QtGui .QWidget ):
55-
56- def __init__ (self ):
99+ def __init__ (self , slider_decimals = 0 ):
57100 super (MainWindowImage , self ).__init__ ()
58101
59102 self .setMouseTracking (True )
@@ -88,15 +131,15 @@ def __init__(self):
88131 # add a QLabel giving information on the current slice and the APR
89132 self .slice_info = Qt .QtGui .QLabel (self )
90133
91- self .slice_info .move (10 , 20 )
92- self .slice_info .setFixedWidth (200 )
134+ self .slice_info .move (20 , 20 )
135+ self .slice_info .setFixedWidth (250 )
93136
94137 # add a label for the current cursor position
95138
96139 self .cursor = Qt .QtGui .QLabel (self )
97140
98141 self .cursor .move (20 , 40 )
99- self .cursor .setFixedWidth (200 )
142+ self .cursor .setFixedWidth (250 )
100143
101144 # add parameter tuning
102145
@@ -108,17 +151,17 @@ def __init__(self):
108151
109152 self .max_label = Qt .QtWidgets .QLabel (self )
110153 self .max_label .setText ("Slider Max" )
111- self .max_label .move (505 , 50 )
154+ self .max_label .move (610 , 50 )
112155
113- self .slider_grad = customSlider (self , "grad_th" )
156+ self .slider_grad = CustomSlider (self , "gradient threshold" , decimals = slider_decimals )
114157 self .slider_grad .move (200 , 80 )
115158 self .slider_grad .connectSlider (self .valuechangeGrad )
116159
117- self .slider_sigma = customSlider (self , "sigma_th" )
160+ self .slider_sigma = CustomSlider (self , "sigma threshold" , decimals = slider_decimals )
118161 self .slider_sigma .move (200 , 110 )
119162 self .slider_sigma .connectSlider (self .valuechangeSigma )
120163
121- self .slider_Ith = customSlider (self , "Ip_th" )
164+ self .slider_Ith = CustomSlider (self , "intensity threshold" , decimals = slider_decimals )
122165 self .slider_Ith .move (200 , 140 )
123166 self .slider_Ith .connectSlider (self .valuechangeIth )
124167
@@ -130,7 +173,6 @@ def __init__(self):
130173 img_ref = 0
131174 par_ref = 0
132175
133-
134176 x_num = 0
135177 z_num = 0
136178 y_num = 0
@@ -149,7 +191,6 @@ def __init__(self):
149191
150192 #parameters to be played with
151193 grad_th = 0
152-
153194 app_ref = 0
154195
155196 def imageHoverEvent (self , event ):
@@ -167,17 +208,14 @@ def imageHoverEvent(self, event):
167208 j = int (np .clip (j , 0 , data .shape [1 ] - 1 ))
168209 val = data [i , j ]
169210
170- text_string = "(y: " + str (i ) + ",x: " + str (j ) + ") val; " + str (val ) + "\n "
171-
211+ text_string = 'x={}, y={}, z={}, value={}\n ' .format (j , i , self .current_view , val )
172212 self .cursor .setText (text_string )
173213
174214 def exitPressed (self ):
175215 self .app_ref .exit ()
176216
177- def updateSliceText (self , slice ):
178-
179- text_string = 'Slice: ' + str (slice ) + '/' + str (self .z_num ) + ", " + str (self .y_num ) + 'x' + str (self .x_num ) + '\n '
180-
217+ def updateSliceText (self , z ):
218+ text_string = 'Slice: {}/{}, {}x{}\n ' .format (z + 1 , self .z_num , self .y_num , self .x_num )
181219 self .slice_info .setText (text_string )
182220
183221 def updatedLUT (self ):
@@ -223,7 +261,6 @@ def setLUT(self, string):
223261
224262 self .img_I_ds .setImage (None , levels = (self .apr_ref .level_max ()- 2 , self .apr_ref .level_max ()), opacity = 0.5 )
225263
226-
227264 def update_slice (self , new_view ):
228265
229266 if (new_view >= 0 ) & (new_view < self .z_num ):
@@ -268,7 +305,6 @@ def valuechangeIth(self):
268305 self .par_ref .Ip_th = size
269306 self .update_slice (self .current_view )
270307
271-
272308 def histogram_updated (self ):
273309
274310 hist_range = self .hist .item .getLevels ()
@@ -278,7 +314,6 @@ def histogram_updated(self):
278314
279315 #self.img_I.setLevels([self.hist_min, self.hist_max], True)
280316
281-
282317 def set_image (self , img , converter ):
283318
284319 self .img_I = pg .ImageItem (img [0 , :, :])
@@ -313,7 +348,7 @@ def set_image(self, img, converter):
313348 self .img_I_ds .setRect (Qt .QtCore .QRectF (self .min_x , self .min_y , self .x_num_ds * 2 , self .y_num_ds * 2 ))
314349 self .img_I .setRect (Qt .QtCore .QRectF (self .min_x , self .min_y , self .x_num , self .y_num ))
315350
316- ## Set up the slide
351+ ## Set up the z slider
317352 self .slider .setMinimum (0 )
318353 self .slider .setMaximum (self .z_num - 1 )
319354 self .slider .setTickPosition (Qt .QtWidgets .QSlider .TicksBothSides )
@@ -323,16 +358,14 @@ def set_image(self, img, converter):
323358
324359 ## Image hover event
325360 self .img_I .hoverEvent = self .imageHoverEvent
326-
327361 self .update_slice (int (self .z_num / 2 ))
328362
329363 def closeEvent (self , event ):
330364 self .pg_win .close ()
331365
332366
333- class InteractiveIO () :
367+ class InteractiveIO :
334368 def __init__ (self ):
335-
336369 # class methods require a QApplication instance - this helps to avoid multiple instances...
337370 self .app = Qt .QtGui .QApplication .instance ()
338371 if self .app is None :
@@ -360,7 +393,7 @@ def save_tiff_file_name(default_name='output.tif'):
360393 file_name = Qt .QtGui .QFileDialog .getSaveFileName (None , "Save TIFF" , default_name , "(*.tif *.tiff)" )
361394 return file_name [0 ]
362395
363- def interactive_apr (self , converter , apr , img ):
396+ def interactive_apr (self , converter , apr , img , slider_decimals = 2 ):
364397
365398 converter .get_apr_step1 (apr , img )
366399
@@ -369,7 +402,7 @@ def interactive_apr(self, converter, apr, img):
369402 pg .setConfigOption ('imageAxisOrder' , 'row-major' )
370403
371404 # Create window with GraphicsView widget
372- win = MainWindowImage ()
405+ win = MainWindowImage (slider_decimals = slider_decimals )
373406 win .show ()
374407 win .apr_ref = apr
375408 win .app_ref = self .app
@@ -389,14 +422,16 @@ def interactive_apr(self, converter, apr, img):
389422 converter .get_apr_step2 (apr , win .par_ref )
390423 return None
391424
392- def find_parameters_interactive (self , converter , apr , img ):
425+ def find_parameters_interactive (self , converter , apr , img , slider_decimals = 2 ):
426+
393427 converter .get_apr_step1 (apr , img )
428+
394429 pg .setConfigOption ('background' , 'w' )
395430 pg .setConfigOption ('foreground' , 'k' )
396431 pg .setConfigOption ('imageAxisOrder' , 'row-major' )
397432
398433 # Create window with GraphicsView widget
399- win = MainWindowImage ()
434+ win = MainWindowImage (slider_decimals = slider_decimals )
400435 win .show ()
401436 win .apr_ref = apr
402437 win .app_ref = self .app
0 commit comments