Source code for impy.viewer.widgets.textedit
from __future__ import annotations
import os
import re
import napari
from qtpy.QtWidgets import (QPlainTextEdit, QWidget, QLineEdit, QGridLayout, QLabel, QHBoxLayout, QCheckBox,
QMenuBar, QMenu, QAction)
from qtpy.QtGui import QFont, QSyntaxHighlighter, QTextCharFormat
from qtpy.QtCore import QRegularExpression, Qt
[docs]def read_txt(viewer:"napari.Viewer", path:str):
with open(path, mode="r") as f:
content = f.read()
title, _ = os.path.splitext(os.path.basename(path))
text = TxtFileWidget(viewer, title=title)
text.initText(content)
return viewer.window.add_dock_widget(text, area="right", name=title)
[docs]class TxtFileWidget(QWidget):
"""
A read-only text viewer widget with JSON-like highlight. Capable of search lines.
"""
def __init__(self, viewer:"napari.Viewer", title:str=None):
super().__init__(viewer.window._qt_window)
self.viewer = viewer
self.title = title
self.setLayout(QGridLayout())
self._add_menu()
self._add_txt_viewer(title)
self._add_filter_line()
[docs] def setText(self, text:str):
self.txtviewer.setPlainText(text)
self.highlighter.setDocument(self.txtviewer.document())
def _add_menu(self):
self.menubar = QMenuBar(self)
self.menu = QMenu("&Menu", self)
wrap = QAction("Wrap", self, checkable=True, checked=False)
wrap.triggered.connect(self.change_wrap_mode)
self.menu.addAction(wrap)
close = QAction("Close", self)
close.triggered.connect(self.delete_self)
self.menu.addAction(close)
self.menubar.addMenu(self.menu)
self.layout().addWidget(self.menubar)
return None
def _add_txt_viewer(self, title):
self.txtviewer = TxtViewer(self, title=title)
self.txtviewer.setReadOnly(True)
self.txtviewer.setFont(QFont("Consolas"))
title is None or self.txtviewer.setDocumentTitle(title)
self.highlighter = WordHighlighter(self.txtviewer.document())
self.highlighter.appendRule(r" [-\+]?\d*(\.\d+)?", fcolor=Qt.yellow)
self.highlighter.appendRule(r"\".*?\"", fcolor=Qt.green)
self.layout().addWidget(self.txtviewer)
def _add_filter_line(self):
wid = QWidget(self)
wid.setLayout(QHBoxLayout())
# add label
label = QLabel(self)
label.setText("Search:")
# add line edit
self.line = QLineEdit(self)
self.line.setToolTip("Search line by line with words or regular expressions.")
@self.line.editingFinished.connect
def _():
text = self.line.text()
if text in ("", ".", ".+", ".*"):
if self.highlighter.nrule > 2:
self.highlighter.popRule(-1)
self.setText(self.original_text)
else:
lines = self.original_text.split("\n")
if self.checkbox.isChecked():
reg = re.compile(".*" + text + ".*")
matched_lines = filter(lambda l: reg.match(l), lines)
else:
matched_lines = filter(lambda l: text in l, lines)
if self.highlighter.nrule > 2:
self.highlighter.popRule(-1)
self.highlighter.appendRule(text, fcolor=Qt.cyan, underline=True, weight=QFont.Bold)
self.setText("\n".join(matched_lines))
# add check box
self.checkbox = QCheckBox(self)
self.checkbox.setText("Regex")
self.checkbox.setToolTip("Use regular expression.")
wid.layout().addWidget(label)
wid.layout().addWidget(self.line)
wid.layout().addWidget(self.checkbox)
self.layout().addWidget(wid)
[docs] def change_wrap_mode(self):
# line wrap mode = 0 -> No wrap
# line wrap mode = 1 -> wrapped
mode = self.txtviewer.lineWrapMode()
self.txtviewer.setLineWrapMode(1-mode)
return None
[docs] def delete_self(self):
dock = self.viewer.window._dock_widgets[self.title]
self.viewer.window.remove_dock_widget(dock)
return None
[docs]class TxtViewer(QPlainTextEdit):
def __init__(self, parent, title:str=None):
super().__init__(parent)
self.setReadOnly(True)
self.setFont(QFont("Consolas"))
self.createStandardContextMenu()
self.setLineWrapMode(0)
title is None or self.setDocumentTitle(title)
[docs]class WordHighlighter(QSyntaxHighlighter):
def __init__(self, doc) -> None:
super().__init__(doc)
self.exps:list[Regex] = []
[docs] def appendRule(self, regex:str, **kwargs):
exp = Regex(regex)
exp.defineFormat(**kwargs)
self.exps.append(exp)
@property
def nrule(self):
return len(self.exps)
[docs] def highlightBlock(self, text:str):
for exp in self.exps:
it = exp.globalMatch(text)
while it.hasNext():
match = it.next()
self.setFormat(match.capturedStart(), match.capturedLength(), exp.format_)
[docs]class Regex(QRegularExpression):
[docs] def defineFormat(self, fcolor=None, bcolor=None, weight=None, underline=False, strikeout=False):
format_ = QTextCharFormat()
fcolor is None or format_.setForeground(fcolor)
bcolor is None or format_.setBackground(bcolor)
weight is None or format_.setFontWeight(weight)
underline and format_.setFontUnderline(True)
strikeout and format_.setFontStrikeOut(True)
self.format_ = format_
return None