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 class BranchesTreeWidget(standard
.TreeWidget
):
121 """A tree widget for displaying branches"""
125 def __init__(self
, context
, parent
=None):
126 standard
.TreeWidget
.__init
__(self
, parent
)
128 self
.context
= context
130 self
.setSelectionMode(QtWidgets
.QAbstractItemView
.SingleSelection
)
131 self
.setHeaderHidden(True)
132 self
.setAlternatingRowColors(False)
133 self
.setColumnCount(1)
134 self
.setExpandsOnDoubleClick(False)
136 self
.current_branch
= None
137 self
.tree_helper
= BranchesTreeHelper(self
)
138 self
.git_helper
= GitHelper(context
)
139 self
.runtask
= qtutils
.RunTask(parent
=self
)
141 self
._visible
= False
142 self
._needs
_refresh
= False
143 self
._tree
_states
= None
145 self
.updated
.connect(self
.refresh
, type=Qt
.QueuedConnection
)
146 context
.model
.updated
.connect(self
.updated
)
148 # Expand items when they are clicked
149 self
.clicked
.connect(self
._toggle
_expanded
)
151 # Checkout branch when double clicked
152 self
.doubleClicked
.connect(self
.checkout_action
)
155 """Refresh the UI widgets to match the current state"""
156 self
._needs
_refresh
= True
160 """Refresh the UI to match the updated state"""
161 # There is no need to refresh the UI when this widget is inactive.
162 if not self
._visible
:
164 model
= self
.context
.model
165 self
.current_branch
= model
.currentbranch
167 self
._tree
_states
= self
._save
_tree
_state
()
168 ellipsis
= icons
.ellipsis()
170 local_tree
= create_tree_entries(model
.local_branches
)
171 local_tree
.basename
= N_('Local')
172 local
= create_toplevel_item(local_tree
, icon
=icons
.branch(), ellipsis
=ellipsis
)
174 remote_tree
= create_tree_entries(model
.remote_branches
)
175 remote_tree
.basename
= N_('Remote')
176 remote
= create_toplevel_item(
177 remote_tree
, icon
=icons
.branch(), ellipsis
=ellipsis
180 tags_tree
= create_tree_entries(model
.tags
)
181 tags_tree
.basename
= N_('Tags')
182 tags
= create_toplevel_item(tags_tree
, icon
=icons
.tag(), ellipsis
=ellipsis
)
185 self
.addTopLevelItems([local
, remote
, tags
])
187 if self
._tree
_states
:
188 self
._load
_tree
_state
(self
._tree
_states
)
189 self
._tree
_states
= None
191 self
._update
_branches
()
193 def showEvent(self
, event
):
194 """Defer updating widgets until the widget is visible"""
195 if not self
._visible
:
197 if self
._needs
_refresh
:
199 return super().showEvent(event
)
201 def _toggle_expanded(self
, index
):
202 """Toggle expanded/collapsed state when items are clicked"""
203 item
= self
.itemFromIndex(index
)
204 if item
and item
.childCount():
205 self
.setExpanded(index
, not self
.isExpanded(index
))
207 def contextMenuEvent(self
, event
):
208 """Build and execute the context menu"""
209 context
= self
.context
210 selected
= self
.selected_item()
213 # Only allow actions on leaf nodes that have a valid refname.
214 if not selected
.refname
:
217 root
= get_toplevel_item(selected
)
218 full_name
= selected
.refname
219 menu
= qtutils
.create_menu(N_('Actions'), self
)
221 visualize_action
= qtutils
.add_action(
222 menu
, N_('Visualize'), self
.visualize_branch_action
224 visualize_action
.setIcon(icons
.visualize())
225 menu
.addAction(visualize_action
)
228 # all branches except current the current branch
229 if full_name
!= self
.current_branch
:
231 qtutils
.add_action(menu
, N_('Checkout'), self
.checkout_action
)
234 if root
.name
== N_('Remote'):
235 label
= N_('Checkout as new branch')
236 action
= self
.checkout_new_branch_action
237 menu
.addAction(qtutils
.add_action(menu
, label
, action
))
239 merge_menu_action
= qtutils
.add_action(
240 menu
, N_('Merge into current branch'), self
.merge_action
242 merge_menu_action
.setIcon(icons
.merge())
243 menu
.addAction(merge_menu_action
)
245 # local and remote branch
246 if root
.name
!= N_('Tags'):
248 if root
.name
== N_('Local'):
249 remote
= gitcmds
.tracked_branch(context
, full_name
)
250 if remote
is not None:
253 pull_menu_action
= qtutils
.add_action(
254 menu
, N_('Pull'), self
.pull_action
256 pull_menu_action
.setIcon(icons
.pull())
257 menu
.addAction(pull_menu_action
)
259 push_menu_action
= qtutils
.add_action(
260 menu
, N_('Push'), self
.push_action
262 push_menu_action
.setIcon(icons
.push())
263 menu
.addAction(push_menu_action
)
265 rename_menu_action
= qtutils
.add_action(
266 menu
, N_('Rename Branch'), self
.rename_action
268 rename_menu_action
.setIcon(icons
.edit())
271 menu
.addAction(rename_menu_action
)
274 if full_name
!= self
.current_branch
:
275 delete_label
= N_('Delete Branch')
276 if root
.name
== N_('Remote'):
277 delete_label
= N_('Delete Remote Branch')
279 delete_menu_action
= qtutils
.add_action(
280 menu
, delete_label
, self
.delete_action
282 delete_menu_action
.setIcon(icons
.discard())
285 menu
.addAction(delete_menu_action
)
287 # manage upstream branches for local branches
288 if root
.name
== N_('Local'):
289 upstream_menu
= menu
.addMenu(N_('Set Upstream Branch'))
290 upstream_menu
.setIcon(icons
.branch())
291 self
.build_upstream_menu(upstream_menu
)
293 menu
.exec_(self
.mapToGlobal(event
.pos()))
295 def build_upstream_menu(self
, menu
):
296 """Build the "Set Upstream Branch" sub-menu"""
297 context
= self
.context
298 model
= context
.model
299 selected_branch
= self
.selected_refname()
307 remote
= gitcmds
.upstream_remote(context
, selected_branch
)
308 upstream
= gitcmds
.tracked_branch(context
, branch
=selected_branch
)
310 if not remote
and 'origin' in model
.remotes
:
314 prefix
= remote
+ '/'
315 for branch
in model
.remote_branches
:
316 if branch
.startswith(prefix
):
317 branches
.append(branch
)
319 other_branches
.append(branch
)
321 # This can be a pretty big list, let's try to split it apart
324 for branch
in model
.remote_branches
:
325 new_branch_remote
, _
= gitcmds
.parse_remote_branch(branch
)
326 if branch_remote
and branch_remote
!= new_branch_remote
:
327 target
= other_branches
328 branch_remote
= new_branch_remote
329 target
.append(branch
)
332 if not other_branches
and len(branches
) > limit
:
333 branches
, other_branches
= (branches
[:limit
], branches
[limit
:])
335 # Add an action for each remote branch
336 current_remote
= remote
338 for branch
in branches
:
339 current_remote
= add_branch_to_menu(
348 # This list could be longer so we tuck it away in a sub-menu.
349 # Selecting a branch from the non-default remote is less common.
352 sub_menu
= menu
.addMenu(N_('Other branches'))
353 for branch
in other_branches
:
354 current_remote
= add_branch_to_menu(
363 def set_upstream(self
, branch
, remote_branch
):
364 """Configure the upstream for a branch"""
365 context
= self
.context
366 remote
, r_branch
= gitcmds
.parse_remote_branch(remote_branch
)
367 if remote
and r_branch
:
368 cmds
.do(cmds
.SetUpstreamBranch
, context
, branch
, remote
, r_branch
)
370 def _save_tree_state(self
):
371 """Save the tree state into a dictionary"""
373 for item
in self
.items():
374 states
.update(self
.tree_helper
.save_state(item
))
377 def _load_tree_state(self
, states
):
378 """Restore expanded items after rebuilding UI widgets"""
379 # Disable animations to eliminate redraw flicker.
380 animated
= self
.isAnimated()
381 self
.setAnimated(False)
383 for item
in self
.items():
384 self
.tree_helper
.load_state(item
, states
.get(item
.name
, {}))
385 self
.tree_helper
.set_current_item()
387 self
.setAnimated(animated
)
389 def _update_branches(self
):
390 """Query branch details using a background task"""
391 context
= self
.context
392 current_branch
= self
.current_branch
393 top_item
= self
.topLevelItem(0)
394 item
= find_by_refname(top_item
, current_branch
)
397 expand_item_parents(item
)
398 item
.setIcon(0, icons
.star())
400 branch_details_task
= BranchDetailsTask(
401 context
, current_branch
, self
.git_helper
404 branch_details_task
, finish
=self
._update
_branches
_finished
407 def _update_branches_finished(self
, task
):
408 """Update the UI with the branch details once the background task completes"""
409 current_branch
, tracked_branch
, ahead
, behind
= task
.result
410 top_item
= self
.topLevelItem(0)
411 item
= find_by_refname(top_item
, current_branch
)
412 if current_branch
and tracked_branch
and item
is not None:
415 status_str
+= f
'{uchr(0x2191)}{ahead}'
418 status_str
+= f
' {uchr(0x2193)}{behind}'
421 item
.setText(0, f
'{item.text(0)}\t{status_str}')
423 def git_action_async(
424 self
, action
, args
, kwarg
=None, update_refs
=False, remote_messages
=False
426 """Execute a git action in a background task"""
429 task
= AsyncGitActionTask(self
.git_helper
, action
, args
, kwarg
, update_refs
)
430 progress
= standard
.progress(
431 N_('Executing action %s') % action
, N_('Updating'), self
434 result_handler
= log
.show_remote_messages(self
.context
, self
)
436 result_handler
= None
441 finish
=self
.git_action_completed
,
442 result
=result_handler
,
445 def git_action_completed(self
, task
):
446 """Update the with the results of an async git action"""
447 status
, out
, err
= task
.result
448 self
.git_helper
.show_result(task
.action
, status
, out
, err
)
450 self
.context
.model
.update_refs()
452 def push_action(self
):
453 """Push the selected branch to its upstream remote"""
454 context
= self
.context
455 branch
= self
.selected_refname()
456 remote_branch
= gitcmds
.tracked_branch(context
, branch
)
457 context
.settings
.load()
458 push_settings
= context
.settings
.get_gui_state_by_name('push')
459 remote_messages
= push_settings
.get('remote_messages', False)
461 remote
, branch_name
= gitcmds
.parse_remote_branch(remote_branch
)
462 if remote
and branch_name
:
463 # we assume that user wants to "Push" the selected local
464 # branch to a remote with same name
465 self
.git_action_async(
467 [remote
, branch_name
],
469 remote_messages
=remote_messages
,
472 def rename_action(self
):
473 """Rename the selected branch"""
474 branch
= self
.selected_refname()
475 new_branch
, ok
= qtutils
.prompt(
476 N_('Enter New Branch Name'), title
=N_('Rename branch'), text
=branch
478 if ok
and new_branch
:
479 self
.git_action_async('rename', [branch
, new_branch
], update_refs
=True)
481 def pull_action(self
):
482 """Pull the selected branch into the current branch"""
483 context
= self
.context
484 branch
= self
.selected_refname()
487 remote_branch
= gitcmds
.tracked_branch(context
, branch
)
488 context
.settings
.load()
489 pull_settings
= context
.settings
.get_gui_state_by_name('pull')
490 remote_messages
= pull_settings
.get('remote_messages', False)
492 remote
, branch_name
= gitcmds
.parse_remote_branch(remote_branch
)
493 if remote
and branch_name
:
494 self
.git_action_async(
496 [remote
, branch_name
],
498 remote_messages
=remote_messages
,
501 def delete_action(self
):
502 """Delete the selected branch"""
503 branch
= self
.selected_refname()
504 if not branch
or branch
== self
.current_branch
:
508 root
= get_toplevel_item(self
.selected_item())
511 if root
.name
== N_('Remote'):
515 remote
, branch
= gitcmds
.parse_remote_branch(branch
)
516 if remote
and branch
:
517 cmds
.do(cmds
.DeleteRemoteBranch
, self
.context
, remote
, branch
)
519 cmds
.do(cmds
.DeleteBranch
, self
.context
, branch
)
521 def merge_action(self
):
522 """Merge the selected branch into the current branch"""
523 branch
= self
.selected_refname()
524 if branch
and branch
!= self
.current_branch
:
525 self
.git_action_async('merge', [branch
])
527 def checkout_action(self
):
528 """Checkout the selected branch"""
529 branch
= self
.selected_refname()
530 if branch
and branch
!= self
.current_branch
:
531 self
.git_action_async('checkout', [branch
], update_refs
=True)
533 def checkout_new_branch_action(self
):
534 """Checkout a new branch"""
535 branch
= self
.selected_refname()
536 if branch
and branch
!= self
.current_branch
:
537 _
, new_branch
= gitcmds
.parse_remote_branch(branch
)
538 self
.git_action_async(
539 'checkout', ['-b', new_branch
, branch
], update_refs
=True
542 def visualize_branch_action(self
):
543 """Visualize the selected branch"""
544 branch
= self
.selected_refname()
546 cmds
.do(cmds
.VisualizeRevision
, self
.context
, branch
)
548 def selected_refname(self
):
549 return getattr(self
.selected_item(), 'refname', None)
552 class BranchDetailsTask(qtutils
.Task
):
553 """Lookup branch details in a background task"""
555 def __init__(self
, context
, current_branch
, git_helper
):
557 self
.context
= context
558 self
.current_branch
= current_branch
559 self
.git_helper
= git_helper
562 """Query git for branch details"""
563 tracked_branch
= gitcmds
.tracked_branch(self
.context
, self
.current_branch
)
567 if self
.current_branch
and tracked_branch
:
568 origin
= tracked_branch
+ '..' + self
.current_branch
569 our_commits
= self
.git_helper
.log(origin
)[STDOUT
]
570 ahead
= our_commits
.count('\n')
574 origin
= self
.current_branch
+ '..' + tracked_branch
575 their_commits
= self
.git_helper
.log(origin
)[STDOUT
]
576 behind
= their_commits
.count('\n')
580 return self
.current_branch
, tracked_branch
, ahead
, behind
583 class BranchTreeWidgetItem(QtWidgets
.QTreeWidgetItem
):
584 def __init__(self
, name
, refname
=None, icon
=None):
585 QtWidgets
.QTreeWidgetItem
.__init
__(self
)
587 self
.refname
= refname
588 self
.setText(0, name
)
589 self
.setToolTip(0, name
)
591 self
.setIcon(0, icon
)
592 self
.setFlags(Qt
.ItemIsEnabled | Qt
.ItemIsSelectable
)
596 """Tree representation for the branches widget
598 The branch widget UI displays the basename. For intermediate names, e.g.
599 "xxx" in the "xxx/abc" and "xxx/def" branches, the 'refname' will be None.
600 'children' contains a list of TreeEntry, and is empty when refname is
605 def __init__(self
, basename
, refname
, children
):
606 self
.basename
= basename
607 self
.refname
= refname
608 self
.children
= children
611 def create_tree_entries(names
):
612 """Create a nested tree structure with a single root TreeEntry.
614 When names == ['xxx/abc', 'xxx/def'] the result will be::
640 # Phase 1: build a nested dictionary representing the intermediate
641 # names in the branches, e.g. {'xxx': {'abc': {}, 'def': {}}}
642 tree_names
= create_name_dict(names
)
644 # Loop over the names again, this time we'll create tree entries
646 root
= TreeEntry(None, None, [])
648 cur_names
= tree_names
649 cur_entries
= entries
651 children
= root
.children
652 for part
in item
.split('/'):
656 tree
, _
= cur_entries
[part
]
659 tree
= TreeEntry(part
, None, [])
660 cur_entries
[part
] = (tree
, {})
661 # Append onto the parent children list only once
662 children
.append(tree
)
664 # This is the actual branch
665 tree
= TreeEntry(part
, item
, [])
666 children
.append(tree
)
667 cur_entries
[part
] = (tree
, {})
669 # Advance into the nested child list
670 children
= tree
.children
671 # Advance into the inner dict
672 cur_names
= cur_names
[part
]
673 _
, cur_entries
= cur_entries
[part
]
678 def create_name_dict(names
):
679 # Phase 1: build a nested dictionary representing the intermediate
680 # names in the branches, e.g. {'xxx': {'abc': {}, 'def': {}}}
683 part_names
= tree_names
684 for part
in item
.split('/'):
685 # Descend into the inner names dict.
686 part_names
= part_names
.setdefault(part
, {})
690 def create_toplevel_item(tree
, icon
=None, ellipsis
=None):
691 """Create a top-level BranchTreeWidgetItem and its children"""
693 item
= BranchTreeWidgetItem(tree
.basename
, icon
=ellipsis
)
694 children
= create_tree_items(tree
.children
, icon
=icon
, ellipsis
=ellipsis
)
696 item
.addChildren(children
)
700 def create_tree_items(entries
, icon
=None, ellipsis
=None):
701 """Create children items for a tree item"""
704 item
= BranchTreeWidgetItem(tree
.basename
, refname
=tree
.refname
, icon
=icon
)
705 children
= create_tree_items(tree
.children
, icon
=icon
, ellipsis
=ellipsis
)
707 item
.addChildren(children
)
708 if ellipsis
is not None:
709 item
.setIcon(0, ellipsis
)
715 def expand_item_parents(item
):
716 """Expand tree parents from item"""
717 parent
= item
.parent()
718 while parent
is not None:
719 if not parent
.isExpanded():
720 parent
.setExpanded(True)
721 parent
= parent
.parent()
724 def find_by_refname(item
, refname
):
725 """Find child by full name recursive"""
728 for i
in range(item
.childCount()):
729 child
= item
.child(i
)
730 if child
.refname
and child
.refname
== refname
:
733 result
= find_by_refname(child
, refname
)
734 if result
is not None:
740 def get_toplevel_item(item
):
741 """Returns top-most item found by traversing up the specified item"""
743 parent
= item
.parent()
745 while parent
is not None:
746 parents
.append(parent
)
747 parent
= parent
.parent()
752 class BranchesTreeHelper
:
753 """Save and restore the tree state"""
755 def __init__(self
, tree
):
757 self
.current_item
= None
759 def set_current_item(self
):
760 """Reset the current item"""
761 if self
.current_item
is not None:
762 self
.tree
.setCurrentItem(self
.current_item
)
763 self
.current_item
= None
765 def load_state(self
, item
, state
):
766 """Load expanded items from a dict"""
769 if state
.get('expanded', False) and not item
.isExpanded():
770 item
.setExpanded(True)
771 if state
.get('selected', False) and not item
.isSelected():
772 item
.setSelected(True)
773 self
.current_item
= item
775 children_state
= state
.get('children', {})
776 if not children_state
:
778 for i
in range(item
.childCount()):
779 child
= item
.child(i
)
780 self
.load_state(child
, children_state
.get(child
.name
, {}))
782 def save_state(self
, item
):
783 """Save the selected and expanded item state into a dict"""
784 expanded
= item
.isExpanded()
785 selected
= item
.isSelected()
788 'children': children
,
789 'expanded': expanded
,
790 'selected': selected
,
792 result
= {item
.name
: entry
}
793 for i
in range(item
.childCount()):
794 child
= item
.child(i
)
795 children
.update(self
.save_state(child
))
801 def __init__(self
, context
):
802 self
.context
= context
803 self
.git
= context
.git
805 def log(self
, origin
):
806 return self
.git
.log(origin
, abbrev
=7, pretty
='format:%h', _readonly
=True)
808 def push(self
, remote
, branch
):
809 return self
.git
.push(remote
, branch
, verbose
=True)
811 def pull(self
, remote
, branch
):
812 return self
.git
.pull(remote
, branch
, no_ff
=True, verbose
=True)
814 def merge(self
, branch
):
815 return self
.git
.merge(branch
, no_commit
=True)
817 def rename(self
, branch
, new_branch
):
818 return self
.git
.branch(branch
, new_branch
, m
=True)
820 def checkout(self
, *args
, **options
):
821 return self
.git
.checkout(*args
, **options
)
824 def show_result(command
, status
, out
, err
):
825 Interaction
.log_status(status
, out
, err
)
827 Interaction
.command_error(N_('Error'), command
, status
, out
, err
)
830 class BranchesFilterWidget(QtWidgets
.QWidget
):
831 def __init__(self
, tree
, parent
=None):
832 QtWidgets
.QWidget
.__init
__(self
, parent
)
835 hint
= N_('Filter branches...')
836 self
.text
= text
.LineEdit(parent
=self
, clear_button
=True)
837 self
.text
.setToolTip(hint
)
838 self
.setFocusProxy(self
.text
)
841 self
.main_layout
= qtutils
.hbox(defs
.no_margin
, defs
.spacing
, self
.text
)
842 self
.setLayout(self
.main_layout
)
844 self
.text
.textChanged
.connect(self
.apply_filter
)
845 self
.tree
.updated
.connect(self
.apply_filter
, type=Qt
.QueuedConnection
)
847 def apply_filter(self
):
848 value
= get(self
.text
)
849 if value
== self
._filter
:
851 self
._apply
_bold
(self
._filter
, False)
854 self
._apply
_bold
(value
, True)
856 def _apply_bold(self
, value
, is_bold
):
857 match
= Qt
.MatchContains | Qt
.MatchRecursive
858 children
= self
.tree
.findItems(value
, match
)
860 for child
in children
:
861 if child
.childCount() == 0:
863 font
.setBold(is_bold
)
864 child
.setFont(0, font
)