137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
"""
|
|
This document contains the tree widget used to display the editor document
|
|
outline.
|
|
|
|
"""
|
|
from pyqode.core.panels import FoldingPanel
|
|
from pyqode.python.modes import DocumentAnalyserMode
|
|
from pyqode.qt import QtCore, QtGui, QtWidgets
|
|
from pyqode.core.api import TextBlockHelper, TextHelper, TextBlockUserData
|
|
|
|
|
|
class PyOutlineTreeWidget(QtWidgets.QTreeWidget):
|
|
"""
|
|
Displays the outline of a PyCodeEdit. The treeview is fully synced with
|
|
the fold panel.
|
|
|
|
To use the widget, you just have to set the active editor using
|
|
:func:`PyOutlineTreeWidget.set_editor`.
|
|
|
|
"""
|
|
def __init__(self, parent=None):
|
|
super(PyOutlineTreeWidget, self).__init__(parent)
|
|
self._editor = None
|
|
self._analyser = None
|
|
self._folding_panel = None
|
|
self._expanded_items = []
|
|
self.setHeaderHidden(True)
|
|
self.itemClicked.connect(self._on_item_clicked)
|
|
self.itemCollapsed.connect(self._on_item_state_changed)
|
|
self.itemExpanded.connect(self._on_item_state_changed)
|
|
self._updating = True
|
|
|
|
def set_editor(self, editor):
|
|
"""
|
|
Sets the current editor. The widget display the structure of that
|
|
editor.
|
|
|
|
:param editor: PyCodeEdit
|
|
"""
|
|
if self._analyser:
|
|
try:
|
|
self._analyser.document_changed.disconnect(self._on_changed)
|
|
except (TypeError, RuntimeError):
|
|
pass
|
|
if self._folding_panel:
|
|
try:
|
|
self._folding_panel.trigger_state_changed.disconnect(
|
|
self._on_block_state_changed)
|
|
except (TypeError, RuntimeError):
|
|
pass
|
|
self._editor = editor
|
|
if self._editor is not None:
|
|
try:
|
|
self._folding_panel = editor.panels.get(FoldingPanel)
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
self._folding_panel.trigger_state_changed.connect(
|
|
self._on_block_state_changed)
|
|
try:
|
|
analyser = editor.modes.get(DocumentAnalyserMode)
|
|
except KeyError:
|
|
self._analyser = None
|
|
else:
|
|
assert isinstance(analyser, DocumentAnalyserMode)
|
|
self._analyser = analyser
|
|
analyser.document_changed.connect(self._on_changed)
|
|
self._on_changed()
|
|
|
|
def _on_item_state_changed(self, item):
|
|
if self._updating:
|
|
return
|
|
block = item.data(0, QtCore.Qt.UserRole).block
|
|
assert isinstance(item, QtWidgets.QTreeWidgetItem)
|
|
item_state = not item.isExpanded()
|
|
block_state = TextBlockHelper.get_fold_trigger_state(block)
|
|
if item_state != block_state:
|
|
self._updating = True
|
|
self._folding_panel.toggle_fold_trigger(block)
|
|
self._updating = False
|
|
|
|
def _on_block_state_changed(self, block, state):
|
|
if self._updating:
|
|
return
|
|
data = block.userData()
|
|
if data is not None:
|
|
try:
|
|
item_state = not data.tree_item.isExpanded()
|
|
if item_state != state:
|
|
if state:
|
|
self.collapseItem(data.tree_item)
|
|
else:
|
|
self.expandItem(data.tree_item)
|
|
except AttributeError:
|
|
# a block that is not represented in the tree view has
|
|
# folded/unfolded, just ignore it
|
|
pass
|
|
|
|
def _on_changed(self):
|
|
"""
|
|
Update the tree items
|
|
"""
|
|
self._updating = True
|
|
to_collapse = []
|
|
self.clear()
|
|
if self._editor and self._analyser and self._folding_panel:
|
|
items, to_collapse = self._analyser.to_tree_widget_items(
|
|
to_collapse=to_collapse)
|
|
if len(items):
|
|
self.addTopLevelItems(items)
|
|
self.expandAll()
|
|
for item in reversed(to_collapse):
|
|
self.collapseItem(item)
|
|
self._updating = False
|
|
return
|
|
# no data
|
|
root = QtWidgets.QTreeWidgetItem()
|
|
root.setText(0, 'No data')
|
|
root.setIcon(0, QtGui.QIcon.fromTheme(
|
|
'dialog-information',
|
|
QtGui.QIcon(':/pyqode-icons/rc/dialog-info.png')))
|
|
self.addTopLevelItem(root)
|
|
self._updating = False
|
|
|
|
def _on_item_clicked(self, item):
|
|
"""
|
|
Go to the item position in the editor.
|
|
"""
|
|
if item:
|
|
name = item.data(0, QtCore.Qt.UserRole)
|
|
if name:
|
|
go = name.block.blockNumber()
|
|
helper = TextHelper(self._editor)
|
|
if helper.current_line_nbr() != go:
|
|
helper.goto_line(go, column=name.column)
|
|
self._editor.setFocus()
|