@@ -83,6 +83,13 @@ def __init__(self, parent=None):
8383 self .histogram .setImageItem (self .image_item )
8484 self .histogram .item .gradient .loadPreset ('viridis' )
8585 layout .addWidget (self .histogram ,0 )
86+
87+ # get the context menu of the plot widget and add an option to transpose the map
88+ menu = self .plot_widget .getPlotItem ().getViewBox ().getMenu (None )
89+ action_logscale = QAction ("Log Scale" ,self )
90+ menu .insertAction (menu .actions ()[1 ],action_logscale )
91+ action_logscale .triggered .connect (self .toggle_log_scale )
92+
8693
8794 self .plot_widget .getPlotItem ().mouseDoubleClickEvent = self .image_double_clicked
8895
@@ -120,6 +127,15 @@ def removeHLine(self, index=-1):
120127 self .sigHLineRemoved .emit (index )
121128 self .active_line = self .h_lines [- 1 ] if self .h_lines else None # Set the active line to the last one if available
122129
130+ def toggle_log_scale (self ):
131+ """Toggle logarithmic scale for the heatmap."""
132+ if self .image_item .image is not None :
133+ im = self .image_item .image
134+ if self .use_log_scale :
135+ im = 10 ** im
136+ self .use_log_scale = not self .use_log_scale
137+ self .set_data (self .x , im )
138+
123139 def set_data (self , x ,z ,y = None ):
124140 """Set the data for the heatmap."""
125141 self .n = z .shape [1 ]
@@ -496,11 +512,12 @@ def __init__(self, parent=None):
496512 self .plot_widget .getPlotItem ().addItem (self .hkl_text_item )
497513 self .hkl_text_item .setVisible (False ) # Hide the text item by default
498514 # initialize the plot limits
499- self .plot_widget .setLimits (xMin = - 1 , xMax = 181 )
515+ self .plot_widget .setLimits (xMin = - 1 , xMax = 181 , yMin = - 1e4 , yMax = 1e8 )
500516
501517 self .plot_widget .sigXRangeChanged .connect (self .xrange_changed )
502518
503519 self .plot_widget .getPlotItem ().vb .hoverEvent = self .hover_event
520+ self .plot_widget .getPlotItem ().vb .mouseDoubleClickEvent = self .mouse_double_clicked
504521
505522 self .set_xlabel ("radial axis" )
506523 self .set_ylabel ("intensity" )
@@ -578,6 +595,8 @@ def __init__(self, parent=None):
578595 action .setMenu (menu )
579596 action .triggered .connect (lambda : menu .exec (QCursor .pos ()))
580597
598+ self .plot_widget .getPlotItem ().ctrl .logYCheck .toggled .connect (self .rescale_all_references )
599+
581600
582601 def add_pattern (self ):
583602 """Add a new pattern item to the plot."""
@@ -695,12 +714,10 @@ def _set_reference_data(self, item, x, I):
695714 """Set the data for a reference pattern item."""
696715 x = np .repeat (x ,2 )
697716 I = np .repeat (I ,2 )
698- I [::2 ] = 0 # Set the intensity to 0 for the first point of each pair
699- if self .y is None :
700- scale = 100
701- else :
702- scale = self .y .max () if self .y .max ()> 0 else 1.
703- item .setData (x , I * scale ) # Update with new data
717+ I [::2 ] = 1e-10
718+ item .setData (x , I ) # Update with new data
719+ self ._rescale_reference (item ) # Rescale the reference pattern to match the current y-axis scale
720+
704721
705722 def remove_reference (self , index = - 1 ):
706723 """Remove a reference pattern from the plot."""
@@ -714,16 +731,46 @@ def toggle_reference(self, index, is_checked):
714731 reference_item .setVisible (is_checked )
715732 self .hkl_text_item .setVisible (False ) # Hide the text item when toggling reference visibility
716733
734+ def rescale_all_references (self ):
735+ """Rescale the intensity of all reference patterns to the current y-max."""
736+ for reference_item in self .reference_items :
737+ self ._rescale_reference (reference_item )
738+
717739 def rescale_reference (self ,index ):
718740 """Rescale the intensity of the indexed reference to the current y-max"""
719741 reference_item = self .reference_items [index ]
742+ self ._rescale_reference (reference_item )
743+
744+ def _rescale_reference (self , reference_item ):
720745 x , I = reference_item .getData ()
721- I /= I .max () # Normalize the intensity to the maximum value
722- if self .y is None or len (self .y ) == 0 :
723- scale = 100
746+ # check if the yxais is in log mode
747+ _y_axis = self .get_axis ('y' )
748+ if _y_axis .logMode :
749+ # bring back to linear scale
750+ I = 10 ** I
751+ I = I - I .min ()
752+ # normalize
753+ I /= I .max ()
754+ # scale
755+ if self .y is None or len (self .y ) == 0 :
756+ _y_min , _y_max = [10 ** r for r in _y_axis .range ]
757+ else :
758+ _y = self .y [self .y > 0 ]
759+ _y_min = _y .min ()* .9
760+ _y_max = _y .max ()
761+ I = I * (_y_max - _y_min )+ _y_min
762+
763+ reference_item .setData (x , I ) # Rescale the reference pattern
764+
724765 else :
725- scale = self .y .max ()
726- reference_item .setData (x , I * scale ) # Rescale the reference pattern
766+ I /= I .max () # Normalize the intensity to the maximum value
767+ I [::2 ] = 1e-10
768+ if self .y is None or len (self .y ) == 0 :
769+ scale = 100
770+ else :
771+ scale = self .y .max ()
772+ reference_item .setData (x , I * scale ) # Rescale the reference pattern
773+
727774
728775 def reference_clicked (self , item , event ):
729776 """Handle the click event on a reference pattern."""
@@ -741,8 +788,17 @@ def reference_clicked(self, item, event):
741788 # Show the hkl indices in the text item
742789 self .hkl_text_item .setColor (color )
743790 self .hkl_text_item .setText (f"({ hkl } )" )
744- self .hkl_text_item .setPos (x_hkls [idx ], 0 )
791+ self .hkl_text_item .setPos (x_hkls [idx ], y )
745792 self .hkl_text_item .setVisible (True ) # Show the text item
793+
794+ def toggle_log_y (self , checked ):
795+ """Toggle logarithmic scale for the y-axis."""
796+ if checked :
797+ self .plot_widget .setLogMode (y = True )
798+ self .plot_widget .setLimits (yMin = - 8 , yMax = 8 )
799+ else :
800+ self .plot_widget .setLogMode (y = False )
801+ self .plot_widget .setLimits (yMin = - 1e4 , yMax = 1e8 )
746802
747803 def set_avg_data (self , y_avg ):
748804 """Set the average data for the pattern."""
@@ -753,11 +809,11 @@ def set_avg_data(self, y_avg):
753809
754810 def set_xlabel (self , label ):
755811 """Set the x-axis label."""
756- self .plot_widget . getPlotItem (). getAxis ( 'bottom ' ).setLabel (label )
812+ self .get_axis ( 'x ' ).setLabel (label )
757813
758814 def set_ylabel (self , label ):
759815 """Set the y-axis label."""
760- self .plot_widget . getPlotItem (). getAxis ( 'left ' ).setLabel (label )
816+ self .get_axis ( 'y ' ).setLabel (label )
761817
762818 def set_xrange (self , x_range ):
763819 """Set the x-axis range."""
@@ -769,7 +825,6 @@ def set_xrange(self, x_range):
769825 self .plot_widget .setXRange (x_min , x_max , padding = 0 )
770826 # reconnect the signal
771827 self .plot_widget .sigXRangeChanged .connect (self .xrange_changed )
772- #self.plot_widget.getPlotItem().getAxis('bottom').setRange(x_min, x_max)
773828
774829 def xrange_changed (self ,vb , x_range ):
775830 """Handle the x-axis range change."""
@@ -813,7 +868,10 @@ def update_fill_area(self):
813868 if roi is None or np .sum (roi ) == 0 :
814869 return
815870 y = self .y [roi ]
816- y0 = np .zeros_like (y )
871+ if self .get_log_mode ('y' ):
872+ y0 = self .y [self .y > 0 ].min () * np .ones_like (y )
873+ else :
874+ y0 = np .zeros_like (y )
817875 if self .linear_region_ignore_negative :
818876 y = np .clip (y , 0 , None )
819877 if self .linear_region_linear_background :
@@ -867,6 +925,17 @@ def toggle_linear_region_options(self,button):
867925 self .update_fill_area ()
868926 self .sigLinearRegionChangedFinished .emit (self .get_linear_region_roi ())
869927
928+ def get_axis (self , axis ):
929+ """Get the specified axis of the plot."""
930+ axis_names = {'x' : 'bottom' , 'bottom' : 'bottom' , 'y' : 'left' , 'left' : 'left' }
931+ if axis in axis_names :
932+ axis = axis_names [axis ]
933+ return self .plot_widget .getPlotItem ().getAxis (axis )
934+
935+ def get_log_mode (self , axis = 'y' ):
936+ """Get the log mode of the specified axis."""
937+ return self .get_axis (axis ).logMode
938+
870939 def hover_event (self , event ):
871940 """Handle the hover event on the plot item."""
872941 if not event .isExit ():
@@ -878,12 +947,26 @@ def hover_event(self, event):
878947 pos = self .plot_widget .getPlotItem ().vb .mapToView (pos )
879948 x = pos .x ()
880949 y = pos .y ()
950+ if self .get_log_mode ('y' ):
951+ y = 10 ** y
881952 # Emit the signal with the x and y coordinates
882953 self .sigPatternHovered .emit ((x , y ))
883954 else :
884955 # emit None to indicate the mouse is no longer hovering
885956 self .sigPatternHovered .emit (None ) # Emit None to indicate no hover
886957
958+ def mouse_double_clicked (self , event ):
959+ """Handle the mouse double click event on the plot item."""
960+ if self .lr .isVisible ():
961+ pos = event .pos ()
962+ # # Convert the position to the plot item's coordinates
963+ x = self .plot_widget .getPlotItem ().vb .mapToView (pos ).x ()
964+ x_min , x_max = self .lr .getRegion ()
965+ width = x_max - x_min
966+ new_x_min = x - width / 2
967+ new_x_max = x + width / 2
968+ self .lr .setRegion ([new_x_min , new_x_max ])
969+
887970 def set_color_cycle (self ,color_cycle ):
888971 """Set the color cycle for the plot items."""
889972 self .color_cycle = color_cycle
@@ -922,8 +1005,8 @@ def updateForeground(self):
9221005 Update the foreground color of the plot widget to the current default
9231006 from pyqtgraph configOptions.
9241007 """
925- x_axis = self .plot_widget . getPlotItem (). getAxis ( 'bottom ' )
926- y_axis = self .plot_widget . getPlotItem (). getAxis ( 'left ' )
1008+ x_axis = self .get_axis ( 'x ' )
1009+ y_axis = self .get_axis ( 'y ' )
9271010
9281011 x_axis .setPen ()
9291012 x_axis .setTextPen ()
@@ -1127,7 +1210,8 @@ class BasicMapWidget(QWidget):
11271210 def __init__ (self , parent = None ):
11281211 super ().__init__ (parent )
11291212 self .fnames = [] # used to keep track of which dataset is used
1130- self .transpose = False
1213+ self .transposed = False
1214+ self .log_scale = False
11311215
11321216 # Create a layout
11331217 vlayout = QVBoxLayout (self )
@@ -1181,13 +1265,21 @@ def __init__(self, parent=None):
11811265 menu .insertAction (menu .actions ()[1 ],action_transpose )
11821266 action_transpose .triggered .connect (self .toggle_transpose )
11831267
1268+ # add a log scale action to the context menu
1269+ action_log_scale = QAction ("Log scale" ,self )
1270+ action_log_scale .setCheckable (True )
1271+ menu .insertAction (menu .actions ()[2 ],action_log_scale )
1272+ action_log_scale .triggered .connect (self .toggle_log_scale )
1273+
11841274
11851275 def set_data (self , im ):
11861276 """Set the data for the map."""
11871277 if im is None :
11881278 return
1189- if self .transpose :
1279+ if self .transposed :
11901280 im = im .T
1281+ if self .log_scale :
1282+ im = np .log10 (im , where = (im > 0 ), out = np .zeros_like (im ))
11911283 self .image_item .setImage (im )
11921284
11931285 def image_double_clicked (self , event ):
@@ -1245,14 +1337,27 @@ def autoRange(self):
12451337 def toggle_transpose (self ):
12461338 """Transpose the map."""
12471339 # new transpose state
1248- _transpose = not self .transpose
1340+ _transpose = not self .transposed
12491341 # always set transpose to True when updating the data
12501342 # to either transpose or "un-transpose" the image
1251- self .transpose = True
1343+ self .transposed = True
12521344 im = self .image_item .image
12531345 self .set_data (im )
12541346 # set the transpose state
1255- self .transpose = _transpose
1347+ self .transposed = _transpose
1348+
1349+ def toggle_log_scale (self , checked ):
1350+ """Toggle logarithmic scale for the color map."""
1351+ im = self .image_item .image
1352+ # if already in log scale, bring back to linear scale before updating the data
1353+ if self .log_scale :
1354+ im = 10 ** im
1355+ # if already transposed, bring back to original orientation before updating the data
1356+ if self .transposed :
1357+ im = im .T
1358+ self .log_scale = checked
1359+ self .set_data (im )
1360+
12561361
12571362class CorrelationMapWidget (BasicMapWidget ):
12581363 """
0 commit comments