2323 QToolButton , QLineEdit , QAction , QActionGroup , QGroupBox ,
2424 QStyledItemDelegate , QStyleOptionViewItem , QStyle , QSizePolicy , QToolTip ,
2525 QDialogButtonBox , QPushButton , QCheckBox , QComboBox , QStackedLayout ,
26- QDialog , QRadioButton , QGridLayout , QLabel , QSpinBox , QDoubleSpinBox )
26+ QDialog , QRadioButton , QGridLayout , QLabel , QSpinBox , QDoubleSpinBox ,
27+ QShortcut
28+ )
2729from AnyQt .QtGui import QStandardItemModel , QStandardItem , QKeySequence , QIcon
2830from AnyQt .QtCore import (
29- Qt , QEvent , QSize , QModelIndex , QAbstractItemModel , QPersistentModelIndex
31+ Qt , QEvent , QSize , QModelIndex , QAbstractItemModel , QPersistentModelIndex ,
32+ QRect
3033)
3134from AnyQt .QtCore import pyqtSignal as Signal , pyqtSlot as Slot
3235
@@ -1133,16 +1136,25 @@ def __init__(self, *args, **kwargs):
11331136 shortcut = QKeySequence (QKeySequence .Delete ),
11341137 shortcutContext = Qt .WidgetShortcut ,
11351138 )
1136- self .merge_items = QAction (
1137- "M " , group ,
1138- objectName = "action-merge-item " ,
1139+ self .merge_selected_items = QAction (
1140+ "= " , group ,
1141+ objectName = "action-merge-selected-items " ,
11391142 toolTip = "Merge selected items." ,
11401143 shortcut = QKeySequence (Qt .ControlModifier | Qt .Key_Equal ),
1144+ shortcutContext = Qt .WidgetShortcut ,
1145+ enabled = False ,
1146+ )
1147+ self .merge_items = QAction (
1148+ "ƒM" , group ,
1149+ objectName = "action-activate-merge-dialog" ,
1150+ toolTip = "Merge infrequent items." ,
1151+ shortcut = QKeySequence (Qt .ControlModifier | Qt .MetaModifier | Qt .Key_Equal ),
11411152 shortcutContext = Qt .WidgetShortcut
11421153 )
11431154
11441155 self .add_new_item .triggered .connect (self ._add_category )
11451156 self .remove_item .triggered .connect (self ._remove_category )
1157+ self .merge_selected_items .triggered .connect (self ._merge_selected_categories )
11461158 self .merge_items .triggered .connect (self ._merge_categories )
11471159
11481160 button1 = FixedSizeButton (
@@ -1162,18 +1174,27 @@ def __init__(self, *args, **kwargs):
11621174 accessibleName = "Remove"
11631175 )
11641176 button5 = FixedSizeButton (
1177+ self , defaultAction = self .merge_selected_items ,
1178+ accessibleName = "Merge selected items"
1179+ )
1180+ button6 = FixedSizeButton (
11651181 self , defaultAction = self .merge_items ,
1166- accessibleName = "Merge" ,
1182+ accessibleName = "Merge infrequent " ,
11671183 )
1168- self .values_edit .addActions ([self .move_value_up , self .move_value_down ,
1169- self .add_new_item , self .remove_item ])
1184+
1185+ self .values_edit .addActions ([
1186+ self .move_value_up , self .move_value_down ,
1187+ self .add_new_item , self .remove_item , self .merge_selected_items
1188+ ])
11701189 hlayout .addWidget (button1 )
11711190 hlayout .addWidget (button2 )
11721191 hlayout .addSpacing (3 )
11731192 hlayout .addWidget (button3 )
11741193 hlayout .addWidget (button4 )
11751194 hlayout .addSpacing (3 )
11761195 hlayout .addWidget (button5 )
1196+ hlayout .addWidget (button6 )
1197+
11771198 hlayout .addStretch (10 )
11781199 vlayout .addLayout (hlayout )
11791200
@@ -1184,6 +1205,8 @@ def __init__(self, *args, **kwargs):
11841205 QWidget .setTabOrder (button1 , button2 )
11851206 QWidget .setTabOrder (button2 , button3 )
11861207 QWidget .setTabOrder (button3 , button4 )
1208+ QWidget .setTabOrder (button4 , button5 )
1209+ QWidget .setTabOrder (button5 , button6 )
11871210
11881211 def set_data (self , var , transform = ()):
11891212 raise NotImplementedError
@@ -1332,6 +1355,7 @@ def on_value_selection_changed(self):
13321355 len (rows ) > 1 and \
13331356 not any (index .data (EditStateRole ) != ItemEditState .NoState
13341357 for index in rows )
1358+ self .merge_selected_items .setEnabled (enable_merge )
13351359
13361360 if len (rows ) == 1 :
13371361 i = rows [0 ].row ()
@@ -1455,6 +1479,66 @@ def complete_merge(text, merge_attributes):
14551479 dlg .get_merged_value_name (), dlg .get_merge_attributes ()
14561480 )
14571481
1482+ def _merge_selected_categories (self ):
1483+ """
1484+ Merge selected categories into one.
1485+
1486+ Popup an editable combo box for selection/edit of a new value.
1487+ """
1488+ view = self .values_edit
1489+ model = view .model () # type: QAbstractItemModel
1490+ rows = view .selectedIndexes () # type: List[QModelIndex]
1491+ if not len (rows ) >= 2 :
1492+ return # pragma: no cover
1493+ first_row = rows [0 ]
1494+
1495+ def mapRectTo (widget , parent , rect ):
1496+ # type: (QWidget, QWidget, QRect) -> QRect
1497+ return QRect (
1498+ widget .mapTo (parent , rect .topLeft ()),
1499+ rect .size (),
1500+ )
1501+
1502+ def mapRectToGlobal (widget , rect ):
1503+ # type: (QWidget, QRect) -> QRect
1504+ return QRect (
1505+ widget .mapToGlobal (rect .topLeft ()),
1506+ rect .size (),
1507+ )
1508+ view .scrollTo (first_row )
1509+ vport = view .viewport ()
1510+ vrect = view .visualRect (first_row )
1511+ vrect = mapRectTo (vport , view , vrect )
1512+ vrect = vrect .intersected (vport .geometry ())
1513+ vrect = mapRectToGlobal (vport , vrect )
1514+
1515+ cb = QComboBox (editable = True , insertPolicy = QComboBox .InsertAtBottom )
1516+ cb .setAttribute (Qt .WA_DeleteOnClose )
1517+ sh = QShortcut (QKeySequence (QKeySequence .Cancel ), cb )
1518+ sh .activated .connect (cb .close )
1519+ cb .setParent (self , Qt .Popup )
1520+ cb .move (vrect .topLeft ())
1521+
1522+ cb .addItems (
1523+ list (unique (str (row .data (Qt .EditRole )) for row in rows )))
1524+ prows = [QPersistentModelIndex (row ) for row in rows ]
1525+
1526+ def complete_merge (text ):
1527+ # write the new text for edit role in all rows
1528+ with disconnected (model .dataChanged , self .on_values_changed ):
1529+ for prow in prows :
1530+ if prow .isValid ():
1531+ model .setData (QModelIndex (prow ), text , Qt .EditRole )
1532+ cb .close ()
1533+ self .variable_changed .emit ()
1534+
1535+ cb .activated [str ].connect (complete_merge )
1536+ size = cb .sizeHint ().expandedTo (vrect .size ())
1537+ cb .resize (size )
1538+ cb .show ()
1539+ cb .raise_ ()
1540+ cb .setFocus (Qt .PopupFocusReason )
1541+
14581542
14591543class ContinuousVariableEditor (VariableEditor ):
14601544 # TODO: enable editing of display format...
0 commit comments