Files
krowlog/bigtext.py
2021-10-25 13:43:17 +02:00

227 lines
8.3 KiB
Python

import math
import re
from typing import Optional, List
import PyQt6.QtGui
from PyQt6 import QtGui
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtGui import QMouseEvent
from PyQt6.QtWidgets import *
from highlight import Highlight
from highlight_regex import HighlightRegex
from highlight_selection import HighlightSelection
from highlighted_range import HighlightedRange
from line import Line
from logFileModel import LogFileModel
import re
from settings import Settings
class BigText(QWidget):
def __init__(self, model: LogFileModel):
super(BigText, self).__init__()
self.grid = QGridLayout()
self.grid.setContentsMargins(0,0,0,0)
self.grid.setHorizontalSpacing(0)
self.grid.setVerticalSpacing(0)
self.setLayout(self.grid)
big_text = InnerBigText(model, self)
big_text.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding))
self.h_scroll_bar = QScrollBar(Qt.Orientation.Horizontal)
self.h_scroll_bar.setMinimum(0)
self.h_scroll_bar.setMaximum(1)
self.h_scroll_bar.valueChanged.connect(big_text.h_scroll_event)
self.v_scroll_bar = QScrollBar()
self.v_scroll_bar.setPageStep(1)
self.v_scroll_bar.valueChanged.connect(big_text.v_scroll_event)
self.grid.addWidget(big_text,0,0)
self.grid.addWidget(self.h_scroll_bar, 1, 0)
self.grid.addWidget(self.v_scroll_bar, 0, 1)
class InnerBigText(QWidget):
_byte_offset = 0
_left_offset = 0
scroll_lines = 0
longest_line = 0
highlights: [Highlight] = []
def __init__(self, model: LogFileModel, parent: BigText):
super(InnerBigText, self).__init__()
self.model = model
self.parent = parent
self.setFocusPolicy(Qt.FocusPolicy.StrongFocus)
self.font = QFont("monospace", 12)
self.update_font_metrics(QPainter(self))
self.lines = []
self.selection_highlight = HighlightSelection(0, 0)
self.highlights = [
HighlightRegex(
r"INFO",
brush=QBrush(QColor(220, 112, 122)),
brush_full_line=QBrush(QColor(255, 112, 122))
),
self.selection_highlight,
]
def paintEvent(self, event: QPaintEvent) -> None:
self.draw()
def keyPressEvent(self, e: QKeyEvent) -> None:
lines_to_scroll = math.floor(self.lines_shown()) -1
if e.key() == Qt.Key.Key_PageUp:
self.scroll_by_lines(lines_to_scroll)
if e.key() == Qt.Key.Key_PageDown:
self.scroll_by_lines(-lines_to_scroll)
if e.key() == 16777235:
self.scroll_by_lines(3)
if e.key() == 16777237:
self.scroll_by_lines(-3)
def wheelEvent(self, event: QWheelEvent):
direction = 1 if event.angleDelta().y() > 0 else -1
#print("wheel event fired :) %s" % (direction))
self.scroll_by_lines(direction * 3)
def scroll_by_lines(self, scroll_lines: int):
self.scroll_lines = scroll_lines
self.update()
self.parent.v_scroll_bar.setValue(self._byte_offset)
def mousePressEvent(self, e: QtGui.QMouseEvent) -> None:
if e.buttons() == Qt.MouseButton.LeftButton:
offset = self.to_byte_offset(e)
self.selection_highlight.set_start(offset)
self.selection_highlight.set_end_byte(offset)
self.update()
def mouseMoveEvent(self, e: QMouseEvent):
current_byte = self.to_byte_offset(e)
if self.selection_highlight.end_byte != current_byte:
self.selection_highlight.set_end_byte(current_byte)
self.update()
# print("-> %s,%s" %(self._selection_start_byte, self._selection_end_byte))
def h_scroll_event(self, left_offset: int):
self._left_offset = left_offset
#print("left_offset: %d" % left_offset)
self.update()
def v_scroll_event(self, byte_offset: int):
self._byte_offset = byte_offset
self.update()
def update_longest_line(self, length: int):
width_in_chars = self.width() / self.char_width
#print("width_in_chars: %d" % width_in_chars)
if self.longest_line < length:
self.longest_line = length
maximum = max(0, length - width_in_chars+1)
self.parent.h_scroll_bar.setMaximum(maximum)
def to_byte_offset(self, e: QMouseEvent) -> int:
line_number = int(e.pos().y() / self.char_height)
if line_number < len(self.lines):
line = self.lines[line_number]
char_in_line = round(e.pos().x() / self.char_width) + self._left_offset
char_in_line = min(char_in_line, line.length())
# print("%s in line %s" % (char_in_line, line_number))
current_byte = line.byte_offset() + char_in_line
# print("%s + %s = %s" % (line.byte_offset(), char_in_line, current_byte))
else:
current_byte = self.model.byte_count()
return current_byte
def lines_shown(self):
return self.height() / float(self.char_height)
def draw(self):
painter = QPainter(self)
painter.setFont(self.font)
painter.setPen(QColor(0, 0, 0))
self.update_font_metrics(painter)
lines_to_show = self.lines_shown()
#print("%s / %s = %s" %(self.height(), float(self.char_height), lines_to_show))
self.lines = self.model.data(self._byte_offset, self.scroll_lines, lines_to_show)
#print("lines_to_show: %d returned: %d" % (lines_to_show, len(self.lines)))
self.scroll_lines=0
self._byte_offset = self.lines[0].byte_offset()
# document length == maximum + pageStep + aFewBytesSoThatTheLastLineIsShown
self.parent.v_scroll_bar.setMaximum(self.model.byte_count() - 1 - 10)
for l in self.lines:
self.update_longest_line(len(l.line()))
# draw hightlights first - some characters may overlap to the next line
# by drawing the background hightlights first we prevent that the hightlight
# draws over a character
y_line_offset = self.char_height;
for l in self.lines:
for h in self.highlights:
optional_highlight_range = h.compute_highlight(l)
if optional_highlight_range:
for highlight in optional_highlight_range:
self.draw_highlight(highlight, painter, y_line_offset)
y_line_offset = y_line_offset + self.char_height
left_offset = -1*self._left_offset * self.char_width
y_line_offset = self.char_height;
for l in self.lines:
painter.drawText(left_offset, y_line_offset, l.line())
y_line_offset = y_line_offset + self.char_height
painter.end()
def draw_highlight(self, highlight: HighlightedRange, painter: QPainter, y_line_offset: int):
left_offset = -1*self._left_offset * self.char_width
x1 = highlight.get_start() * self.char_width
width = highlight.get_width() * self.char_width
y1 = y_line_offset - self.char_height + self.char_height / 7
height = self.char_height
if highlight.is_highlight_full_line():
full_width = Settings.max_line_length() * self.char_width
rect = QRect(left_offset, y1, full_width, height)
self.highlight_background(painter, rect, highlight.get_brush_full_line())
rect = QRect(left_offset+x1, y1, width, height)
self.highlight_background(painter, rect, highlight.get_brush())
def highlight_background(self, painter: QPainter, rect: QRect, brush: QBrush):
old_brush = painter.brush()
old_pen = painter.pen()
painter.setBrush(brush)
painter.setPen(Qt.PenStyle.NoPen)
painter.drawRoundedRect(rect, 3.0, 3.0)
painter.setBrush(old_brush)
painter.setPen(old_pen)
def update_font_metrics(self, painter: QPainter):
fm: QFontMetrics = painter.fontMetrics()
self.char_height = fm.height()
self.char_width = fm.averageCharWidth() # all chars have same width for monospace font
text = "012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
self.char_width = fm.horizontalAdvance(text) / float(len(text))
#print("font width=%s height=%s" % (self.char_width, self.char_height))