1 """Provides widgets related to branches"""
2 from functools
import partial
4 from qtpy
import QtWidgets
5 from qtpy
.QtCore
import Qt
6 from qtpy
.QtCore
import Signal
8 from ..compat
import uchr
9 from ..git
import STDOUT
11 from ..interaction
import Interaction
12 from ..widgets
import defs
13 from ..widgets
import standard
14 from ..qtutils
import get
16 from .. import gitcmds
17 from .. import hotkeys
19 from .. import qtutils
24 def defer_func(parent
, title
, func
, *args
, **kwargs
):
25 """Return a QAction bound against a partial func with arguments"""
26 return qtutils
.add_action(parent
, title
, partial(func
, *args
, **kwargs
))
29 def add_branch_to_menu(menu
, branch
, remote_branch
, remote
, upstream
, func
):
30 """Add a remote branch to the context menu"""
31 branch_remote
, _
= gitcmds
.parse_remote_branch(remote_branch
)
32 if branch_remote
!= remote
:
34 action
= defer_func(menu
, remote_branch
, func
, branch
, remote_branch
)
35 if remote_branch
== upstream
:
36 action
.setIcon(icons
.star())
37 menu
.addAction(action
)
41 class AsyncGitActionTask(qtutils
.Task
):
42 """Run git action asynchronously"""
44 def __init__(self
, git_helper
, action
, args
, kwarg
, update_refs
):
45 qtutils
.Task
.__init
__(self
)
46 self
.git_helper
= git_helper
50 self
.update_refs
= update_refs
53 """Runs action and captures the result"""
54 git_action
= getattr(self
.git_helper
, self
.action
)
55 return git_action(*self
.args
, **self
.kwarg
)
58 class BranchesWidget(QtWidgets
.QFrame
):
59 """A widget for displaying and performing operations on branches"""
61 def __init__(self
, context
, parent
):
62 QtWidgets
.QFrame
.__init
__(self
, parent
)
63 self
.model
= model
= context
.model
65 tooltip
= N_('Toggle the branches filter')
66 icon
= icons
.ellipsis()
67 self
.filter_button
= qtutils
.create_action_button(tooltip
=tooltip
, icon
=icon
)
71 icons
.reverse_chronological(),
74 'Set the sort order for branches and tags.\n'
75 'Toggle between date-based and version-name-based sorting.'
77 icon
= self
.order_icon(model
.ref_sort
)
78 self
.sort_order_button
= qtutils
.create_action_button(
79 tooltip
=tooltip_order
, icon
=icon
82 self
.tree
= BranchesTreeWidget(context
, parent
=self
)
83 self
.filter_widget
= BranchesFilterWidget(self
.tree
)
84 self
.filter_widget
.hide()
86 self
.setFocusProxy(self
.tree
)
87 self
.setToolTip(N_('Branches'))
89 self
.main_layout
= qtutils
.vbox(
90 defs
.no_margin
, defs
.spacing
, self
.filter_widget
, self
.tree
92 self
.setLayout(self
.main_layout
)
94 self
.toggle_action
= qtutils
.add_action(
95 self
, tooltip
, self
.toggle_filter
, hotkeys
.FILTER
97 qtutils
.connect_button(self
.filter_button
, self
.toggle_filter
)
98 qtutils
.connect_button(
99 self
.sort_order_button
, cmds
.run(cmds
.CycleReferenceSort
, context
)
102 model
.refs_updated
.connect(self
.refresh
, Qt
.QueuedConnection
)
104 def toggle_filter(self
):
105 shown
= not self
.filter_widget
.isVisible()
106 self
.filter_widget
.setVisible(shown
)
108 self
.filter_widget
.setFocus()
112 def order_icon(self
, idx
):
113 return self
.order_icons
[idx
% len(self
.order_icons
)]
116 icon
= self
.order_icon(self
.model
.ref_sort
)
117 self
.sort_order_button
.setIcon(icon
)
120 # pylint: disable=too-many-ancestors
121 class BranchesTreeWidget(standard
.TreeWidget
):
122 """A tree widget for displaying branches"""
126 def __init__(self
, context
, parent
=None):
127 standard
.TreeWidget
.__init
__(self
, parent
)
129 self
.context
= context
131 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
132 self
.setHeaderHidden(True)
133 self
.setAlternatingRowColors(False)
134 self
.setColumnCount(1)
135 self
.setExpandsOnDoubleClick(False)
137 self
.current_branch
= None
138 self
.tree_helper
= BranchesTreeHelper(self
)
139 self
.git_helper
= GitHelper(context
)
140 self
.runtask
= qtutils
.RunTask(parent
=self
)
142 self
._visible
= False
143 self
._needs
_refresh
= False
144 self
._tree
_states
= None
146 self
.updated
.connect(self
.refresh
, type=Qt
.QueuedConnection
)
147 context
.model
.updated
.connect(self
.updated
)
149 # Expand items when they are clicked
150 # pylint: disable=no-member
151 self
.clicked
.connect(self
._toggle
_expanded
)
153 # Checkout branch when double clicked
154 self
.doubleClicked
.connect(self
.checkout_action
)
157 """Refresh the UI widgets to match the current state"""
158 self
._needs
_refresh
= True
162 """Refresh the UI to match the updated state"""
163 # There is no need to refresh the UI when this widget is inactive.
164 if not self
._visible
:
166 model
= self
.context
.model
167 self
.current_branch
= model
.currentbranch
169 self
._tree
_states
= self
._save
_tree
_state
()
170 ellipsis
= icons
.ellipsis()
172 local_tree
= create_tree_entries(model
.local_branches
)
173 local_tree
.basename
= N_('Local')
174 local
= create_toplevel_item(local_tree
, icon
=icons
.branch(), ellipsis
=ellipsis
)
176 remote_tree
= create_tree_entries(model
.remote_branches
)
177 remote_tree
.basename
= N_('Remote')
178 remote
= create_toplevel_item(
179 remote_tree
, icon
=icons
.branch(), ellipsis
=ellipsis
182 tags_tree
= create_tree_entries(model
.tags
)
183 tags_tree
.basename
= N_('Tags')
184 tags
= create_toplevel_item(tags_tree
, icon
=icons
.tag(), ellipsis
=ellipsis
)
187 self
.addTopLevelItems([local
, remote
, tags
])
189 if self
._tree
_states
:
190 self
._load
_tree
_state
(self
._tree
_states
)
191 self
._tree
_states
= None
193 self
._update
_branches
()
195 def showEvent(self
, event
):
196 """Defer updating widgets until the widget is visible"""
197 if not self
._visible
:
199 if self
._needs
_refresh
:
201 return super().showEvent(event
)
203 def _toggle_expanded(self
, index
):
204 """Toggle expanded/collapsed state when items are clicked"""
205 item
= self
.itemFromIndex(index
)
206 if item
and item
.childCount():
207 self
.setExpanded(index
, not self
.isExpanded(index
))
209 def contextMenuEvent(self
, event
):
210 """Build and execute the context menu"""
211 context
= self
.context
212 selected
= self
.selected_item()
215 # Only allow actions on leaf nodes that have a valid refname.
216 if not selected
.refname
:
219 root
= get_toplevel_item(selected
)
220 full_name
= selected
.refname
221 menu
= qtutils
.create_menu(N_('Actions'), self
)
223 visualize_action
= qtutils
.add_action(
224 menu
, N_('Visualize'), self
.visualize_branch_action
226 visualize_action
.setIcon(icons
.visualize())
227 menu
.addAction(visualize_action
)
230 # all branches except current the current branch
231 if full_name
!= self
.current_branch
:
233 qtutils
.add_action(menu
, N_('Checkout'), self
.checkout_action
)
236 if root
.name
== N_('Remote'):
237 label
= N_('Checkout as new branch')
238 action
= self
.checkout_new_branch_action
239 menu
.addAction(qtutils
.add_action(menu
, label
, action
))
241 merge_menu_action
= qtutils
.add_action(
242 menu
, N_('Merge into current branch'), self
.merge_action
244 merge_menu_action
.setIcon(icons
.merge())
245 menu
.addAction(merge_menu_action
)
247 # local and remote branch
248 if root
.name
!= N_('Tags'):
250 if root
.name
== N_('Local'):
251 remote
= gitcmds
.tracked_branch(context
, full_name
)
252 if remote
is not None:
255 pull_menu_action
= qtutils
.add_action(
256 menu
, N_('Pull'), self
.pull_action
258 pull_menu_action
.setIcon(icons
.pull())
259 menu
.addAction(pull_menu_action
)
261 push_menu_action
= qtutils
.add_action(
262 menu
, N_('Push'), self
.push_action
264 push_menu_action
.setIcon(icons
.push())
265 menu
.addAction(push_menu_action
)
267 rename_menu_action
= qtutils
.add_action(
268 menu
, N_('Rename Branch'), self
.rename_action
270 rename_menu_action
.setIcon(icons
.edit())
273 menu
.addAction(rename_menu_action
)
276 if full_name
!= self
.current_branch
:
277 delete_label
= N_('Delete Branch')
278 if root
.name
== N_('Remote'):
279 delete_label
= N_('Delete Remote Branch')
281 delete_menu_action
= qtutils
.add_action(
282 menu
, delete_label
, self
.delete_action
284 delete_menu_action
.setIcon(icons
.discard())
287 menu
.addAction(delete_menu_action
)
289 # manage upstreams for local branches
290 if root
.name
== N_('Local'):
291 upstream_menu
= menu
.addMenu(N_('Set Upstream Branch'))
292 upstream_menu
.setIcon(icons
.branch())
293 self
.build_upstream_menu(upstream_menu
)
295 menu
.exec_(self
.mapToGlobal(event
.pos()))
297 def build_upstream_menu(self
, menu
):
298 """Build the "Set Upstream Branch" sub-menu"""
299 context
= self
.context
300 model
= context
.model
301 selected_branch
= self
.selected_refname()
309 remote
= gitcmds
.upstream_remote(context
, selected_branch
)
310 upstream
= gitcmds
.tracked_branch(context
, branch
=selected_branch
)
312 if not remote
and 'origin' in model
.remotes
:
316 prefix
= remote
+ '/'
317 for branch
in model
.remote_branches
:
318 if branch
.startswith(prefix
):
319 branches
.append(branch
)
321 other_branches
.append(branch
)
323 # This can be a pretty big list, let's try to split it apart
326 for branch
in model
.remote_branches
:
327 new_branch_remote
, _
= gitcmds
.parse_remote_branch(branch
)
328 if branch_remote
and branch_remote
!= new_branch_remote
:
329 target
= other_branches
330 branch_remote
= new_branch_remote
331 target
.append(branch
)
334 if not other_branches
and len(branches
) > limit
:
335 branches
, other_branches
= (branches
[:limit
], branches
[limit
:])
337 # Add an action for each remote branch
338 current_remote
= remote
340 for branch
in branches
:
341 current_remote
= add_branch_to_menu(
350 # This list could be longer so we tuck it away in a sub-menu.
351 # Selecting a branch from the non-default remote is less common.
354 sub_menu
= menu
.addMenu(N_('Other branches'))
355 for branch
in other_branches
:
356 current_remote
= add_branch_to_menu(
365 def set_upstream(self
, branch
, remote_branch
):
366 """Configure the upstream for a branch"""
367 context
= self
.context
368 remote
, r_branch
= gitcmds
.parse_remote_branch(remote_branch
)
369 if remote
and r_branch
:
370 cmds
.do(cmds
.SetUpstreamBranch
, context
, branch
, remote
, r_branch
)
372 def _save_tree_state(self
):
373 """Save the tree state into a dictionary"""
375 for item
in self
.items():
376 states
.update(self
.tree_helper
.save_state(item
))
379 def _load_tree_state(self
, states
):
380 """Restore expanded items after rebuilding UI widgets"""
381 # Disable animations to eliminate redraw flicker.
382 animated
= self
.isAnimated()
383 self
.setAnimated(False)
385 for item
in self
.items():
386 self
.tree_helper
.load_state(item
, states
.get(item
.name
, {}))
387 self
.tree_helper
.set_current_item()
389 self
.setAnimated(animated
)
391 def _update_branches(self
):
392 """Query branch details using a background task"""
393 context
= self
.context
394 current_branch
= self
.current_branch
395 top_item
= self
.topLevelItem(0)
396 item
= find_by_refname(top_item
, current_branch
)
399 expand_item_parents(item
)
400 item
.setIcon(0, icons
.star())
402 branch_details_task
= BranchDetailsTask(
403 context
, current_branch
, self
.git_helper
406 branch_details_task
, finish
=self
._update
_branches
_finished
409 def _update_branches_finished(self
, task
):
410 """Update the UI with the branch details once the background task completes"""
411 current_branch
, tracked_branch
, ahead
, behind
= task
.result
412 top_item
= self
.topLevelItem(0)
413 item
= find_by_refname(top_item
, current_branch
)
414 if current_branch
and tracked_branch
and item
is not None:
417 status_str
+= f
'{uchr(0x2191)}{ahead}'
420 status_str
+= f
' {uchr(0x2193)}{behind}'
423 item
.setText(0, f
'{item.text(0)}\t{status_str}')
425 def git_action_async(
426 self
, action
, args
, kwarg
=None, update_refs
=False, remote_messages
=False
428 """Execute a git action in a background task"""
431 task
= AsyncGitActionTask(self
.git_helper
, action
, args
, kwarg
, update_refs
)
432 progress
= standard
.progress(
433 N_('Executing action %s') % action
, N_('Updating'), self
436 result_handler
= log
.show_remote_messages(self
.context
, self
)
438 result_handler
= None
443 finish
=self
.git_action_completed
,
444 result
=result_handler
,
447 def git_action_completed(self
, task
):
448 """Update the with the results of an async git action"""
449 status
, out
, err
= task
.result
450 self
.git_helper
.show_result(task
.action
, status
, out
, err
)
452 self
.context
.model
.update_refs()
454 def push_action(self
):
455 """Push the selected branch to its upstream remote"""
456 context
= self
.context
457 branch
= self
.selected_refname()
458 remote_branch
= gitcmds
.tracked_branch(context
, branch
)
459 context
.settings
.load()
460 push_settings
= context
.settings
.get_gui_state_by_name('push')
461 remote_messages
= push_settings
.get('remote_messages', False)
463 remote
, branch_name
= gitcmds
.parse_remote_branch(remote_branch
)
464 if remote
and branch_name
:
465 # we assume that user wants to "Push" the selected local
466 # branch to a remote with same name
467 self
.git_action_async(
469 [remote
, branch_name
],
471 remote_messages
=remote_messages
,
474 def rename_action(self
):
475 """Rename the selected branch"""
476 branch
= self
.selected_refname()
477 new_branch
, ok
= qtutils
.prompt(
478 N_('Enter New Branch Name'), title
=N_('Rename branch'), text
=branch
480 if ok
and new_branch
:
481 self
.git_action_async('rename', [branch
, new_branch
], update_refs
=True)
483 def pull_action(self
):
484 """Pull the selected branch into the current branch"""
485 context
= self
.context
486 branch
= self
.selected_refname()
489 remote_branch
= gitcmds
.tracked_branch(context
, branch
)
490 context
.settings
.load()
491 pull_settings
= context
.settings
.get_gui_state_by_name('pull')
492 remote_messages
= pull_settings
.get('remote_messages', False)
494 remote
, branch_name
= gitcmds
.parse_remote_branch(remote_branch
)
495 if remote
and branch_name
:
496 self
.git_action_async(
498 [remote
, branch_name
],
500 remote_messages
=remote_messages
,
503 def delete_action(self
):
504 """Delete the selected branch"""
505 branch
= self
.selected_refname()
506 if not branch
or branch
== self
.current_branch
:
510 root
= get_toplevel_item(self
.selected_item())
513 if root
.name
== N_('Remote'):
517 remote
, branch
= gitcmds
.parse_remote_branch(branch
)
518 if remote
and branch
:
519 cmds
.do(cmds
.DeleteRemoteBranch
, self
.context
, remote
, branch
)
521 cmds
.do(cmds
.DeleteBranch
, self
.context
, branch
)
523 def merge_action(self
):
524 """Merge the selected branch into the current branch"""
525 branch
= self
.selected_refname()
526 if branch
and branch
!= self
.current_branch
:
527 self
.git_action_async('merge', [branch
])
529 def checkout_action(self
):
530 """Checkout the selected branch"""
531 branch
= self
.selected_refname()
532 if branch
and branch
!= self
.current_branch
:
533 self
.git_action_async('checkout', [branch
], update_refs
=True)
535 def checkout_new_branch_action(self
):
536 """Checkout a new branch"""
537 branch
= self
.selected_refname()
538 if branch
and branch
!= self
.current_branch
:
539 _
, new_branch
= gitcmds
.parse_remote_branch(branch
)
540 self
.git_action_async(
541 'checkout', ['-b', new_branch
, branch
], update_refs
=True
544 def visualize_branch_action(self
):
545 """Visualize the selected branch"""
546 branch
= self
.selected_refname()
548 cmds
.do(cmds
.VisualizeRevision
, self
.context
, branch
)
550 def selected_refname(self
):
551 return getattr(self
.selected_item(), 'refname', None)
554 class BranchDetailsTask(qtutils
.Task
):
555 """Lookup branch details in a background task"""
557 def __init__(self
, context
, current_branch
, git_helper
):
559 self
.context
= context
560 self
.current_branch
= current_branch
561 self
.git_helper
= git_helper
564 """Query git for branch details"""
565 tracked_branch
= gitcmds
.tracked_branch(self
.context
, self
.current_branch
)
569 if self
.current_branch
and tracked_branch
:
570 origin
= tracked_branch
+ '..' + self
.current_branch
571 our_commits
= self
.git_helper
.log(origin
)[STDOUT
]
572 ahead
= our_commits
.count('\n')
576 origin
= self
.current_branch
+ '..' + tracked_branch
577 their_commits
= self
.git_helper
.log(origin
)[STDOUT
]
578 behind
= their_commits
.count('\n')
582 return self
.current_branch
, tracked_branch
, ahead
, behind
585 class BranchTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
586 def __init__(self
, name
, refname
=None, icon
=None):
587 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
589 self
.refname
= refname
590 self
.setText(0, name
)
591 self
.setToolTip(0, name
)
593 self
.setIcon(0, icon
)
594 self
.setFlags(Qt
.ItemIsEnabled | Qt
.ItemIsSelectable
)
598 """Tree representation for the branches widget
600 The branch widget UI displays the basename. For intermediate names, e.g.
601 "xxx" in the "xxx/abc" and "xxx/def" branches, the 'refname' will be None.
602 'children' contains a list of TreeEntry, and is empty when refname is
607 def __init__(self
, basename
, refname
, children
):
608 self
.basename
= basename
609 self
.refname
= refname
610 self
.children
= children
613 def create_tree_entries(names
):
614 """Create a nested tree structure with a single root TreeEntry.
616 When names == ['xxx/abc', 'xxx/def'] the result will be::
642 # Phase 1: build a nested dictionary representing the intermediate
643 # names in the branches. e.g. {'xxx': {'abc': {}, 'def': {}}}
644 tree_names
= create_name_dict(names
)
646 # Loop over the names again, this time we'll create tree entries
648 root
= TreeEntry(None, None, [])
650 cur_names
= tree_names
651 cur_entries
= entries
653 children
= root
.children
654 for part
in item
.split('/'):
658 tree
, _
= cur_entries
[part
]
661 tree
= TreeEntry(part
, None, [])
662 cur_entries
[part
] = (tree
, {})
663 # Append onto the parent children list only once
664 children
.append(tree
)
666 # This is the actual branch
667 tree
= TreeEntry(part
, item
, [])
668 children
.append(tree
)
669 cur_entries
[part
] = (tree
, {})
671 # Advance into the nested child list
672 children
= tree
.children
673 # Advance into the inner dict
674 cur_names
= cur_names
[part
]
675 _
, cur_entries
= cur_entries
[part
]
680 def create_name_dict(names
):
681 # Phase 1: build a nested dictionary representing the intermediate
682 # names in the branches. e.g. {'xxx': {'abc': {}, 'def': {}}}
685 part_names
= tree_names
686 for part
in item
.split('/'):
687 # Descend into the inner names dict.
688 part_names
= part_names
.setdefault(part
, {})
692 def create_toplevel_item(tree
, icon
=None, ellipsis
=None):
693 """Create a top-level BranchTreeWidgetItem and its children"""
695 item
= BranchTreeWidgetItem(tree
.basename
, icon
=ellipsis
)
696 children
= create_tree_items(tree
.children
, icon
=icon
, ellipsis
=ellipsis
)
698 item
.addChildren(children
)
702 def create_tree_items(entries
, icon
=None, ellipsis
=None):
703 """Create children items for a tree item"""
706 item
= BranchTreeWidgetItem(tree
.basename
, refname
=tree
.refname
, icon
=icon
)
707 children
= create_tree_items(tree
.children
, icon
=icon
, ellipsis
=ellipsis
)
709 item
.addChildren(children
)
710 if ellipsis
is not None:
711 item
.setIcon(0, ellipsis
)
717 def expand_item_parents(item
):
718 """Expand tree parents from item"""
719 parent
= item
.parent()
720 while parent
is not None:
721 if not parent
.isExpanded():
722 parent
.setExpanded(True)
723 parent
= parent
.parent()
726 def find_by_refname(item
, refname
):
727 """Find child by full name recursive"""
730 for i
in range(item
.childCount()):
731 child
= item
.child(i
)
732 if child
.refname
and child
.refname
== refname
:
735 result
= find_by_refname(child
, refname
)
736 if result
is not None:
742 def get_toplevel_item(item
):
743 """Returns top-most item found by traversing up the specified item"""
745 parent
= item
.parent()
747 while parent
is not None:
748 parents
.append(parent
)
749 parent
= parent
.parent()
754 class BranchesTreeHelper
:
755 """Save and restore the tree state"""
757 def __init__(self
, tree
):
759 self
.current_item
= None
761 def set_current_item(self
):
762 """Reset the current item"""
763 if self
.current_item
is not None:
764 self
.tree
.setCurrentItem(self
.current_item
)
765 self
.current_item
= None
767 def load_state(self
, item
, state
):
768 """Load expanded items from a dict"""
771 if state
.get('expanded', False) and not item
.isExpanded():
772 item
.setExpanded(True)
773 if state
.get('selected', False) and not item
.isSelected():
774 item
.setSelected(True)
775 self
.current_item
= item
777 children_state
= state
.get('children', {})
778 if not children_state
:
780 for i
in range(item
.childCount()):
781 child
= item
.child(i
)
782 self
.load_state(child
, children_state
.get(child
.name
, {}))
784 def save_state(self
, item
):
785 """Save the selected and expanded item state into a dict"""
786 expanded
= item
.isExpanded()
787 selected
= item
.isSelected()
790 'children': children
,
791 'expanded': expanded
,
792 'selected': selected
,
794 result
= {item
.name
: entry
}
795 for i
in range(item
.childCount()):
796 child
= item
.child(i
)
797 children
.update(self
.save_state(child
))
803 def __init__(self
, context
):
804 self
.context
= context
805 self
.git
= context
.git
807 def log(self
, origin
):
808 return self
.git
.log(origin
, abbrev
=7, pretty
='format:%h', _readonly
=True)
810 def push(self
, remote
, branch
):
811 return self
.git
.push(remote
, branch
, verbose
=True)
813 def pull(self
, remote
, branch
):
814 return self
.git
.pull(remote
, branch
, no_ff
=True, verbose
=True)
816 def merge(self
, branch
):
817 return self
.git
.merge(branch
, no_commit
=True)
819 def rename(self
, branch
, new_branch
):
820 return self
.git
.branch(branch
, new_branch
, m
=True)
822 def checkout(self
, *args
, **options
):
823 return self
.git
.checkout(*args
, **options
)
826 def show_result(command
, status
, out
, err
):
827 Interaction
.log_status(status
, out
, err
)
829 Interaction
.command_error(N_('Error'), command
, status
, out
, err
)
832 class BranchesFilterWidget(QtWidgets
.QWidget
):
833 def __init__(self
, tree
, parent
=None):
834 QtWidgets
.QWidget
.__init
__(self
, parent
)
837 hint
= N_('Filter branches...')
838 self
.text
= text
.LineEdit(parent
=self
, clear_button
=True)
839 self
.text
.setToolTip(hint
)
840 self
.setFocusProxy(self
.text
)
843 self
.main_layout
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
, self
.text
)
844 self
.setLayout(self
.main_layout
)
846 # pylint: disable=no-member
847 self
.text
.textChanged
.connect(self
.apply_filter
)
848 self
.tree
.updated
.connect(self
.apply_filter
, type=Qt
.QueuedConnection
)
850 def apply_filter(self
):
851 value
= get(self
.text
)
852 if value
== self
._filter
:
854 self
._apply
_bold
(self
._filter
, False)
857 self
._apply
_bold
(value
, True)
859 def _apply_bold(self
, value
, is_bold
):
860 match
= Qt
.MatchContains | Qt
.MatchRecursive
861 children
= self
.tree
.findItems(value
, match
)
863 for child
in children
:
864 if child
.childCount() == 0:
866 font
.setBold(is_bold
)
867 child
.setFont(0, font
)