55from bisect import bisect_left
66from operator import attrgetter
77
8- from AnyQt .QtCore import Qt , QSize , pyqtSignal as Signal
8+ from AnyQt .QtCore import Qt , QSize , pyqtSignal as Signal , QSortFilterProxyModel
99from AnyQt .QtGui import QStandardItemModel , QStandardItem , QColor , QBrush , QPen
1010from AnyQt .QtWidgets import (
1111 QTableView , QGraphicsTextItem , QGraphicsRectItem , QGraphicsView , QDialog ,
12- QVBoxLayout
12+ QVBoxLayout , QLineEdit
1313)
1414from Orange .data import Variable
1515from Orange .widgets import gui
16- from Orange .widgets .gui import HorizontalGridDelegate
16+ from Orange .widgets .gui import HorizontalGridDelegate , TableBarItem
1717from Orange .widgets .utils .messages import WidgetMessagesMixin
1818from Orange .widgets .utils .progressbar import ProgressBarMixin
1919from Orange .widgets .widget import Msg
@@ -38,6 +38,7 @@ class VizRankDialog(QDialog, ProgressBarMixin, WidgetMessagesMixin):
3838 - `on_selection_changed` that handles event triggered when the user selects
3939 a table row. The method should emit signal
4040 `VizRankDialog.selectionChanged(object)`.
41+ - `bar_length` returns the length of the bar corresponding to the score.
4142
4243 The class provides a table and a button. A widget constructs a single
4344 instance of this dialog in its `__init__`, like (in Sieve) by using a
@@ -95,13 +96,25 @@ def __init__(self, master):
9596 self .saved_progress = 0
9697 self .scores = []
9798
99+ self .filter = QLineEdit ()
100+ self .filter .setPlaceholderText ("Filter ..." )
101+ self .filter .textChanged .connect (self .filter_changed )
102+ self .layout ().addWidget (self .filter )
103+ # Remove focus from line edit
104+ self .setFocus (Qt .ActiveWindowFocusReason )
105+
98106 self .rank_model = QStandardItemModel (self )
107+ self .model_proxy = QSortFilterProxyModel (self )
108+ self .model_proxy .setSourceModel (self .rank_model )
99109 self .rank_table = view = QTableView (
100110 selectionBehavior = QTableView .SelectRows ,
101111 selectionMode = QTableView .SingleSelection ,
102112 showGrid = False )
103- view .setItemDelegate (HorizontalGridDelegate ())
104- view .setModel (self .rank_model )
113+ if self ._has_bars :
114+ view .setItemDelegate (TableBarItem ())
115+ else :
116+ view .setItemDelegate (HorizontalGridDelegate ())
117+ view .setModel (self .model_proxy )
105118 view .selectionModel ().selectionChanged .connect (
106119 self .on_selection_changed )
107120 view .horizontalHeader ().setStretchLastSection (True )
@@ -111,6 +124,10 @@ def __init__(self, master):
111124 self .button = gui .button (
112125 self , self , "Start" , callback = self .toggle , default = True )
113126
127+ @property
128+ def _has_bars (self ):
129+ return type (self ).bar_length is not VizRankDialog .bar_length
130+
114131 @classmethod
115132 def add_vizrank (cls , widget , master , button_label , set_attr_callback ):
116133 """
@@ -177,6 +194,9 @@ def initialize(self):
177194 self .button .setText ("Start" )
178195 self .button .setEnabled (self .check_preconditions ())
179196
197+ def filter_changed (self , text ):
198+ self .model_proxy .setFilterFixedString (text )
199+
180200 def stop_and_reset (self , reset_method = None ):
181201 if self .keep_running :
182202 self .scheduled_call = reset_method or self .initialize
@@ -234,6 +254,12 @@ def compute_score(self, state):
234254 """
235255 raise NotImplementedError
236256
257+ def bar_length (self , score ):
258+ """Compute the bar length (between 0 and 1) corresponding to the score.
259+ Return `None` if the score cannot be normalized.
260+ """
261+ return None
262+
237263 def row_for_state (self , score , state ):
238264 """
239265 Abstract method that return the items that are inserted into the table.
@@ -264,8 +290,12 @@ def run(self):
264290 score = self .compute_score (state )
265291 if score is not None :
266292 pos = bisect_left (self .scores , score )
267- self .rank_model .insertRow (
268- pos , self .row_for_state (score , state ))
293+ row_items = self .row_for_state (score , state )
294+ if self ._has_bars :
295+ bar = self .bar_length (score )
296+ if bar is not None :
297+ row_items [0 ].setData (bar , gui .TableBarItem .BarRole )
298+ self .rank_model .insertRow (pos , row_items )
269299 self .scores .insert (pos , score )
270300 progress .advance ()
271301 self ._select_first_if_none ()
@@ -370,7 +400,10 @@ def check_preconditions(self):
370400 return can_rank
371401
372402 def on_selection_changed (self , selected , deselected ):
373- attrs = [selected .indexes ()[i ].data (self ._AttrRole ) for i in (0 , 1 )]
403+ selection = selected .indexes ()
404+ if not selection :
405+ return
406+ attrs = selected .indexes ()[0 ].data (self ._AttrRole )
374407 self .selectionChanged .emit (attrs )
375408
376409 def state_count (self ):
@@ -385,13 +418,10 @@ def iterate_states(self, initial_state):
385418 sj = 0
386419
387420 def row_for_state (self , score , state ):
388- items = []
389421 attrs = sorted ((self .attrs [x ] for x in state ), key = attrgetter ("name" ))
390- for attr in attrs :
391- item = QStandardItem (attr .name )
392- item .setData (attr , self ._AttrRole )
393- items .append (item )
394- return items
422+ item = QStandardItem (", " .join (a .name for a in attrs ))
423+ item .setData (attrs , self ._AttrRole )
424+ return [item ]
395425
396426
397427class CanvasText (QGraphicsTextItem ):
0 commit comments