11#!/usr/bin/python
22from .. import QtCore , QtWidgets , QtGui
3-
43from .stylesheet import STYLE_TABSEARCH , STYLE_TABSEARCH_LIST , STYLE_QMENU
4+ from collections import OrderedDict
5+ import re
56
67
78class TabSearchCompleter (QtWidgets .QCompleter ):
@@ -112,6 +113,18 @@ def set_nodes(self, node_dict=None):
112113 self ._completer .setModel (self ._model )
113114
114115
116+ def fuzzyFinder (key , collection ):
117+ suggestions = []
118+ pattern = '.*?' .join (key .lower ())
119+ regex = re .compile (pattern )
120+ for item in collection :
121+ match = regex .search (item .lower ())
122+ if match :
123+ suggestions .append ((len (match .group ()), match .start (), item ))
124+
125+ return [x for _ , _ , x in sorted (suggestions )]
126+
127+
115128class TabSearchMenuWidget (QtWidgets .QLineEdit ):
116129 search_submitted = QtCore .Signal (str )
117130
@@ -132,7 +145,7 @@ def __init__(self, parent=None, node_dict=None):
132145 self .SearchMenu .addAction (searchWidget )
133146 self .SearchMenu .setStyleSheet (STYLE_QMENU )
134147
135- self ._actions = []
148+ self ._actions = {}
136149 self ._menus = {}
137150 self ._searched_actions = []
138151
@@ -151,30 +164,32 @@ def _on_text_changed(self,text):
151164
152165 self ._set_menu_visible (False )
153166
154- self ._searched_actions = [action for action in self ._actions \
155- if text .lower () in action .text ().lower ()]
167+ action_names = fuzzyFinder (text , self ._actions .keys ())
156168
169+ self ._searched_actions = [self ._actions [name ] for name in action_names ]
157170 self .SearchMenu .addActions (self ._searched_actions )
158171
172+ if self ._searched_actions :
173+ self .SearchMenu .setActiveAction (self ._searched_actions [0 ])
174+
159175 def _clear_actions (self ):
160176 for action in self ._searched_actions :
161177 self .SearchMenu .removeAction (action )
162178 self ._searched_actions = []
163179
164- def _set_menu_visible (self ,visible ):
165- for menu in self ._menus .values ():
166- menu .menuAction ().setVisible (visible )
180+ def _set_menu_visible (self , visible ):
181+ [menu .menuAction ().setVisible (visible ) for menu in self ._menus .values ()]
167182
168183 def _close (self ):
169184 self ._set_menu_visible (False )
170185 self .SearchMenu .setVisible (False )
171186 self .SearchMenu .menuAction ().setVisible (False )
172187
173188 def _show (self ):
174- self .SearchMenu .exec_ (QtGui .QCursor .pos ())
175189 self .setText ("" )
176190 self .setFocus ()
177191 self ._set_menu_visible (True )
192+ self .SearchMenu .exec_ (QtGui .QCursor .pos ())
178193
179194 def _on_search_submitted (self ):
180195 action = self .sender ()
@@ -192,33 +207,52 @@ def _on_search_submitted(self):
192207
193208 self ._close ()
194209
195- def _generate_items_from_node_dict (self ):
196- node_names = sorted (self ._node_dict .keys ())
210+ def build_menu_tree (self ):
197211 node_types = sorted (self ._node_dict .values ())
212+ node_names = sorted (self ._node_dict .keys ())
213+ menu_tree = OrderedDict ()
198214
199- self ._menus .clear ()
200- self ._actions .clear ()
201- self ._searched_actions .clear ()
202-
215+ max_depth = 0
203216 for node_type in node_types :
204- menu_name = "." .join (node_type .split ("." )[:- 1 ])
205- if menu_name not in self ._menus .keys ():
206- new_menu = QtWidgets .QMenu (menu_name )
207- new_menu .setStyleSheet (STYLE_QMENU )
208- self ._menus [menu_name ] = new_menu
209- self .SearchMenu .addMenu (new_menu )
217+ trees = node_type .split ("." )
218+ trees .pop (- 1 )
219+ for depth , menu_name in enumerate (trees ):
220+ menu_path = "." .join (trees [:depth + 1 ])
221+ if depth in menu_tree .keys ():
222+ if menu_name not in menu_tree [depth ].keys ():
223+ new_menu = QtWidgets .QMenu (menu_name )
224+ new_menu .setStyleSheet (STYLE_QMENU )
225+ menu_tree [depth ][menu_path ] = new_menu
226+ else :
227+ new_menu = QtWidgets .QMenu (menu_name )
228+ new_menu .setStyleSheet (STYLE_QMENU )
229+ menu_tree [depth ] = {menu_path : new_menu }
230+ if depth > 0 :
231+ new_menu .parentPath = "." .join (trees [:depth ])
232+
233+ max_depth = max (max_depth ,depth )
234+
235+ for i in range (max_depth + 1 ):
236+ menus = menu_tree [i ]
237+ for menu_path , menu in menus .items ():
238+ self ._menus [menu_path ] = menu
239+ if i == 0 :
240+ self .SearchMenu .addMenu (menu )
241+ else :
242+ parentMenu = self ._menus [menu .parentPath ]
243+ parentMenu .addMenu (menu )
210244
211245 for name in node_names :
212246 action = QtWidgets .QAction (name , self )
213247 action .setText (name )
214248 action .triggered .connect (self ._on_search_submitted )
215- self ._actions . append ( action )
249+ self ._actions [ name ] = action
216250
217251 menu_name = self ._node_dict [name ]
218- menu_name = "." .join (menu_name .split ("." )[:- 1 ])
252+ menu_path = "." .join (menu_name .split ("." )[:- 1 ])
219253
220- if menu_name in self ._menus .keys ():
221- self ._menus [menu_name ].addAction (action )
254+ if menu_path in self ._menus .keys ():
255+ self ._menus [menu_path ].addAction (action )
222256 else :
223257 self .SearchMenu .addAction (action )
224258
@@ -231,7 +265,7 @@ def set_nodes(self, node_dict=None):
231265 continue
232266 for node_id in node_types :
233267 self ._node_dict ['{} ({})' .format (name , node_id )] = node_id
234- self ._generate_items_from_node_dict ()
268+ self .build_menu_tree ()
235269
236270 self ._show ()
237271
0 commit comments