7474from itertools import chain
7575
7676import numpy as np
77- from qtpy .QtCore import Qt , QPoint , QItemSelection , QItemSelectionModel , Signal
78- from qtpy .QtGui import QDoubleValidator , QIntValidator , QKeySequence , QFontMetrics , QCursor
77+ from qtpy .QtCore import Qt , QPoint , QItemSelection , QItemSelectionModel , Signal , QSize
78+ from qtpy .QtGui import (QDoubleValidator , QIntValidator , QKeySequence , QFontMetrics , QCursor , QPixmap , QPainter ,
79+ QLinearGradient , QColor , QIcon )
7980from qtpy .QtWidgets import (QApplication , QTableView , QItemDelegate , QLineEdit , QCheckBox ,
8081 QMessageBox , QMenu , QLabel , QSpinBox , QWidget , QToolTip , QShortcut , QScrollBar ,
81- QHBoxLayout , QVBoxLayout , QGridLayout , QSizePolicy , QFrame )
82+ QHBoxLayout , QVBoxLayout , QGridLayout , QSizePolicy , QFrame , QComboBox )
8283
8384try :
8485 import xlwings as xw
8586except ImportError :
8687 xw = None
8788
8889from larray_editor .utils import (keybinding , create_action , clear_layout , get_font , from_qvariant , to_qvariant ,
89- is_number , is_float , _ , ima )
90+ is_number , is_float , _ , ima , LinearGradient )
9091from larray_editor .arrayadapter import LArrayDataAdapter
9192from larray_editor .arraymodel import LabelsArrayModel , DataArrayModel
9293from larray_editor .combo import FilterComboBox , FilterMenu
@@ -505,10 +506,30 @@ def __init__(self, parent, data_scrollbar):
505506 self .rangeChanged .connect (data_scrollbar .setRange )
506507
507508
509+ available_gradients = [
510+ # Hue, Saturation, Value, Alpha-channel
511+ ('red-blue' , LinearGradient ([(0 , [0.99 , 0.7 , 1.0 , 0.6 ]), (1 , [0.66 , 0.7 , 1.0 , 0.6 ])])),
512+ ('blue-red' , LinearGradient ([(0 , [0.66 , 0.7 , 1.0 , 0.6 ]), (1 , [0.99 , 0.7 , 1.0 , 0.6 ])])),
513+ ('red-white-blue' , LinearGradient ([(0 , [.99 , .85 , 1. , .6 ]),
514+ (0.5 - 1e-16 , [.99 , .15 , 1. , .6 ]),
515+ (0.5 , [1. , 0. , 1. , 1. ]),
516+ (0.5 + 1e-16 , [.66 , .15 , 1. , .6 ]),
517+ (1 , [.66 , .85 , 1. , .6 ])])),
518+ ('blue-white-red' , LinearGradient ([(0 , [.66 , .85 , 1. , .6 ]),
519+ (0.5 - 1e-16 , [.66 , .15 , 1. , .6 ]),
520+ (0.5 , [1. , 0. , 1. , 1. ]),
521+ (0.5 + 1e-16 , [.99 , .15 , 1. , .6 ]),
522+ (1 , [.99 , .85 , 1. , .6 ])])),
523+ ]
524+ gradient_map = dict (available_gradients )
525+
526+
527+
508528class ArrayEditorWidget (QWidget ):
509- def __init__ (self , parent , data = None , readonly = False , bg_value = None , bg_gradient = None ,
529+ def __init__ (self , parent , data = None , readonly = False , bg_value = None , bg_gradient = 'blue-red' ,
510530 minvalue = None , maxvalue = None ):
511531 QWidget .__init__ (self , parent )
532+ assert bg_gradient in gradient_map
512533 readonly = np .isscalar (data )
513534 self .readonly = readonly
514535
@@ -613,10 +634,33 @@ def __init__(self, parent, data=None, readonly=False, bg_value=None, bg_gradient
613634 self .scientific_checkbox = scientific
614635 btn_layout .addWidget (scientific )
615636
616- bgcolor = QCheckBox (_ ('Background color' ))
617- bgcolor .stateChanged .connect (self .model_data .bgcolor )
618- self .bgcolor_checkbox = bgcolor
619- btn_layout .addWidget (bgcolor )
637+ gradient_chooser = QComboBox ()
638+ gradient_chooser .setMaximumSize (120 , 20 )
639+ gradient_chooser .setIconSize (QSize (100 , 20 ))
640+
641+ pixmap = QPixmap (100 , 15 )
642+ pixmap .fill (Qt .white )
643+ gradient_chooser .addItem (QIcon (pixmap ), " " )
644+
645+ pixmap .fill (Qt .transparent )
646+ painter = QPainter (pixmap )
647+ for name , gradient in available_gradients :
648+ qgradient = gradient .as_qgradient ()
649+
650+ # * fill with white because gradient can be transparent and if we do not "start from whilte", it skews the
651+ # colors.
652+ # * 1 and 13 instead of 0 and 15 to have a transparent border around/between the gradients
653+ painter .fillRect (0 , 1 , 100 , 13 , Qt .white )
654+ painter .fillRect (0 , 1 , 100 , 13 , qgradient )
655+ gradient_chooser .addItem (QIcon (pixmap ), name , gradient )
656+
657+ # without this, we can crash python :)
658+ del painter , pixmap
659+ # select default gradient
660+ gradient_chooser .setCurrentText (bg_gradient )
661+ gradient_chooser .currentIndexChanged .connect (self .gradient_changed )
662+ btn_layout .addWidget (gradient_chooser )
663+ self .gradient_chooser = gradient_chooser
620664
621665 # Set widget layout
622666 layout = QVBoxLayout ()
@@ -625,11 +669,16 @@ def __init__(self, parent, data=None, readonly=False, bg_value=None, bg_gradient
625669 layout .addLayout (btn_layout )
626670 layout .setContentsMargins (0 , 0 , 0 , 0 )
627671 self .setLayout (layout )
628- self .set_data (data , bg_value = bg_value , bg_gradient = bg_gradient )
672+ self .set_data (data , bg_value = bg_value )
673+ self .model_data .set_bg_gradient (gradient_map [bg_gradient ])
629674
630675 # See http://doc.qt.io/qt-4.8/qt-draganddrop-fridgemagnets-dragwidget-cpp.html for an example
631676 self .setAcceptDrops (True )
632677
678+ def gradient_changed (self , index ):
679+ gradient = self .gradient_chooser .itemData (index ) if index > 0 else None
680+ self .model_data .set_bg_gradient (gradient )
681+
633682 def mousePressEvent (self , event ):
634683 self .dragLabel = self .childAt (event .pos ()) if event .button () == Qt .LeftButton else None
635684 self .dragStartPosition = event .pos ()
@@ -692,7 +741,10 @@ def dropEvent(self, event):
692741 new_axes = la_data .axes .copy ()
693742 new_axes .insert (new_index , new_axes .pop (new_axes [previous_index ]))
694743 la_data = la_data .transpose (new_axes )
695- self .set_data (la_data , self .model_data .bg_gradient , self .model_data .bg_value )
744+ bg_value = self .data_adapter .bg_value
745+ if bg_value is not None :
746+ bg_value = bg_value .transpose (new_axes )
747+ self .set_data (la_data , bg_value )
696748
697749 event .setDropAction (Qt .MoveAction )
698750 event .accept ()
@@ -701,9 +753,9 @@ def dropEvent(self, event):
701753 else :
702754 event .ignore ()
703755
704- def set_data (self , data = None , bg_gradient = None , bg_value = None ):
756+ def set_data (self , data = None , bg_value = None ):
705757 # update adapter
706- self .data_adapter .set_data (data , bg_gradient = bg_gradient , bg_value = bg_value )
758+ self .data_adapter .set_data (data , bg_value = bg_value )
707759 la_data = self .data_adapter .get_data ()
708760 axes = la_data .axes
709761 display_names = axes .display_names
@@ -756,8 +808,7 @@ def _update_digits_scientific(self, data):
756808 self .scientific_checkbox .setChecked (use_scientific )
757809 self .scientific_checkbox .setEnabled (is_number (dtype ))
758810
759- self .bgcolor_checkbox .setChecked (self .model_data .bgcolor_enabled )
760- self .bgcolor_checkbox .setEnabled (self .model_data .bgcolor_enabled )
811+ self .gradient_chooser .setEnabled (self .model_data .bgcolor_possible )
761812
762813 def choose_scientific (self , data ):
763814 # max_digits = self.get_max_digits()
0 commit comments