@@ -309,7 +309,24 @@ def __init__(
309309 self .mdi_area .setVerticalScrollBarPolicy (QtCore .Qt .ScrollBarPolicy .ScrollBarAsNeeded )
310310 self .splitter .addWidget (self .mdi_area )
311311
312+ self .channel_search_update_timer = QtCore .QTimer (self )
313+ self .channel_search_update_timer .setSingleShot (True )
314+ self .channel_search_update_timer .setInterval (200 ) # ms delay
315+ self .channel_search_update_timer .timeout .connect (partial (self ._update_inline_search , widget = self .channels_tree ))
316+ self .channels_search_name .textChanged .connect (self .channel_search_update_timer .start )
317+ self .channels_search_name .pattern = None
318+ self .channels_search_bus .textChanged .connect (self .channel_search_update_timer .start )
319+ self .channels_search_bus .pattern = None
312320 self .channels_tree .itemDoubleClicked .connect (self .show_info )
321+
322+ self .filter_search_update_timer = QtCore .QTimer (self )
323+ self .filter_search_update_timer .setSingleShot (True )
324+ self .filter_search_update_timer .setInterval (200 ) # ms delay
325+ self .filter_search_update_timer .timeout .connect (partial (self ._update_inline_search , widget = self .filter_tree ))
326+ self .filter_search_name .textChanged .connect (self .filter_search_update_timer .start )
327+ self .filter_search_name .pattern = None
328+ self .filter_search_bus .textChanged .connect (self .filter_search_update_timer .start )
329+ self .filter_search_bus .pattern = None
313330 self .filter_tree .itemDoubleClicked .connect (self .show_info )
314331
315332 self .channel_view .setCurrentIndex (- 1 )
@@ -534,6 +551,49 @@ def update_all_channel_trees(self):
534551 for widget in widgetList :
535552 self ._update_channel_tree (widget = widget )
536553
554+ def _update_inline_search (self , widget = None ):
555+ # Move all regex logic here
556+ # Mark search bar as red if regex is not valid
557+ # If both are valid, then parse and call _update_channel_tree
558+ if widget is self .channels_tree :
559+ search_name = self .channels_search_name
560+ search_bus = self .channels_search_bus
561+ else :
562+ search_name = self .filter_search_name
563+ search_bus = self .filter_search_bus
564+ do_update = True
565+
566+ valid_text_color = QtGui .QPalette ()
567+ valid_text_color .setColor (QtGui .QPalette .Text , QtCore .Qt .white )
568+ error_text_color = QtGui .QPalette ()
569+ error_text_color .setColor (QtGui .QPalette .Text , QtCore .Qt .red )
570+ try :
571+ name_pattern = ".*" + search_name .text ().strip () + ".*"
572+ if name_pattern .islower ():
573+ name_pattern = re .compile (name_pattern , re .IGNORECASE )
574+ else :
575+ name_pattern = re .compile (name_pattern )
576+ search_name .pattern = name_pattern
577+ search_name .setPalette (valid_text_color )
578+ except re .error :
579+ search_name .setPalette (error_text_color )
580+ do_update = False
581+
582+ try :
583+ bus_pattern = ".*" + search_bus .text ().strip () + ".*"
584+ if bus_pattern .islower ():
585+ bus_pattern = re .compile (bus_pattern , re .IGNORECASE )
586+ else :
587+ bus_pattern = re .compile (bus_pattern )
588+ search_bus .pattern = bus_pattern
589+ search_bus .setPalette (valid_text_color )
590+ except re .error :
591+ search_bus .setPalette (error_text_color )
592+ do_update = False
593+
594+ if do_update :
595+ self ._update_channel_tree (widget = widget )
596+
537597 def _update_channel_tree (self , index = None , widget = None , force = False ):
538598 if widget is None :
539599 widget = self .channels_tree
@@ -544,6 +604,8 @@ def _update_channel_tree(self, index=None, widget=None, force=False):
544604 return
545605
546606 view = self .channel_view if widget is self .channels_tree else self .filter_view
607+ search_name = self .channels_search_name if widget is self .channels_tree else self .filter_search_name
608+ search_bus = self .channels_search_bus if widget is self .channels_tree else self .filter_search_bus
547609
548610 iterator = QtWidgets .QTreeWidgetItemIterator (widget )
549611 signals = set ()
@@ -567,12 +629,24 @@ def _update_channel_tree(self, index=None, widget=None, force=False):
567629 widget .mode = view .currentText ()
568630
569631 if widget .mode == "Natural sort" :
632+ search_name .setVisible (True )
633+ search_bus .setVisible (True )
634+
570635 items = []
571636 for i , group in enumerate (self .mdf .groups ):
572637 for j , ch in enumerate (group .channels ):
573638 entry = i , j
574639
575- channel = MinimalTreeItem (entry , ch .name , strings = [ch .name ], origin_uuid = self .uuid )
640+ # Limit search to 10k items and apply search filters
641+ if len (items ) >= 10_000 :
642+ break
643+ if search_name .pattern and not search_name .pattern .search (ch .name ):
644+ continue
645+ bus = group .channel_group .acq_source .path or "None"
646+ if search_bus .pattern and not search_bus .pattern .search (bus ):
647+ continue
648+
649+ channel = MinimalTreeItem (entry , ch .name , strings = [f"{ ch .name } —— { bus :<10} " ], origin_uuid = self .uuid )
576650 channel .setToolTip (0 , f"{ ch .name } @ group { i } , index { j } " )
577651
578652 if entry in signals :
@@ -589,8 +663,10 @@ def _update_channel_tree(self, index=None, widget=None, force=False):
589663 widget .addTopLevelItems (items )
590664
591665 elif widget .mode == "Internal file structure" :
592- items = []
666+ search_name .setVisible (False )
667+ search_bus .setVisible (False )
593668
669+ items = []
594670 for i , group in enumerate (self .mdf .groups ):
595671 entry = i , 0xFFFFFFFFFFFFFFFF
596672
@@ -663,6 +739,10 @@ def _update_channel_tree(self, index=None, widget=None, force=False):
663739 widget .addTopLevelItems (items )
664740
665741 else :
742+ # widget.mode == "Selected channels only"
743+ search_name .setVisible (False )
744+ search_bus .setVisible (False )
745+
666746 items = []
667747 for entry in signals :
668748 gp_index , ch_index = entry
@@ -1663,7 +1743,7 @@ def close(self):
16631743
16641744 if windows :
16651745 unsaved = False
1666-
1746+
16671747 if hexdigest :
16681748 worker = md5 ()
16691749 try :
0 commit comments