add positions of hits to the range slider

This commit is contained in:
2024-03-28 20:21:07 +01:00
parent 76f7baecf3
commit 6538e85f37
5 changed files with 65 additions and 4 deletions

View File

@@ -38,7 +38,8 @@ class FilterTask(QRunnable):
on_before: Callable[[], None], on_before: Callable[[], None],
on_finish: Callable[[], None], on_finish: Callable[[], None],
show_only_matches: bool, show_only_matches: bool,
matches_separator: str matches_separator: str,
zoned_plugin_registry: ZonedPluginRegistry
): ):
super(FilterTask, self).__init__() super(FilterTask, self).__init__()
self.source_model = source_model self.source_model = source_model
@@ -53,6 +54,7 @@ class FilterTask(QRunnable):
self.filter_match_found_listeners = filter_match_found_listeners self.filter_match_found_listeners = filter_match_found_listeners
self.show_only_matches = show_only_matches self.show_only_matches = show_only_matches
self.matches_separator = matches_separator self.matches_separator = matches_separator
self.zoned_plugin_registry = zoned_plugin_registry
def only_matches(self, line: str, regex: re.Pattern): def only_matches(self, line: str, regex: re.Pattern):
result = "" result = ""
@@ -87,7 +89,10 @@ class FilterTask(QRunnable):
listener(-1, -1) # notify listeners that a new search started listener(-1, -1) # notify listeners that a new search started
hits_count = 0 hits_count = 0
hits_positions: set[float] = set(())
self.zoned_plugin_registry.execute("update_hit_positions", hits_positions)
last_progress_report = time.time() last_progress_report = time.time()
source_file_size = self.source_model.byte_count()
try: try:
with open(self.source_model.get_file(), "rb") as source: with open(self.source_model.get_file(), "rb") as source:
source.seek(self.source_model.range_start) source.seek(self.source_model.range_start)
@@ -121,6 +126,12 @@ class FilterTask(QRunnable):
target.write(line.encode("utf8")) target.write(line.encode("utf8"))
hits_count = hits_count + 1 hits_count = hits_count + 1
hits_positions_before = len(hits_positions)
hits_positions.add(round(source_line_offset / source_file_size, 3))
hits_positions_after = len(hits_positions)
if hits_positions_before != hits_positions_after:
self.zoned_plugin_registry.execute("update_hit_positions", hits_positions)
# sometime buffering can hide results for a while # sometime buffering can hide results for a while
# We force a flush periodically. # We force a flush periodically.
if line_count % 10000 == 0: if line_count % 10000 == 0:
@@ -350,6 +361,7 @@ class FilterWidget(QWidget):
lambda: self.search_is_running.emit(True), lambda: self.search_is_running.emit(True),
lambda: self.search_is_running.emit(False), lambda: self.search_is_running.emit(False),
show_only_matches, show_only_matches,
self.matches_separator.text() self.matches_separator.text(),
self._zoned_plugin_registry
) )
QThreadPool.globalInstance().start(self.filter_task) QThreadPool.globalInstance().start(self.filter_task)

View File

@@ -1,6 +1,7 @@
from PySide6.QtWidgets import * from PySide6.QtWidgets import *
from PySide6.QtCore import * from PySide6.QtCore import *
from src.pluginbase import PluginBase
from src.ui.bigtext.bigtext import BigText from src.ui.bigtext.bigtext import BigText
from src.plugins.logfile.filterviewsyncer import FilterViewSyncer from src.plugins.logfile.filterviewsyncer import FilterViewSyncer
from src.plugins.logfile.filterwidget import FilterWidget from src.plugins.logfile.filterwidget import FilterWidget
@@ -17,6 +18,7 @@ class FullTabWidget(Tab):
self._model = model self._model = model
self._zoned_plugin_registry = zoned_plugin_registry self._zoned_plugin_registry = zoned_plugin_registry
self.file_view = BigText(model) self.file_view = BigText(model)
self._zoned_plugin_registry.register_plugin("TabWidgetPlugin", TabWidgetPlugin(self.file_view))
self.filter_hit_view = FilterWidget(self._model, self._zoned_plugin_registry) self.filter_hit_view = FilterWidget(self._model, self._zoned_plugin_registry)
self.filter_view_syncer = FilterViewSyncer(self.file_view) self.filter_view_syncer = FilterViewSyncer(self.file_view)
self.filter_hit_view.add_line_click_listener(self.filter_view_syncer.click_listener) self.filter_hit_view.add_line_click_listener(self.filter_view_syncer.click_listener)
@@ -51,3 +53,12 @@ class FullTabWidget(Tab):
# overriding abstract method # overriding abstract method
def on_reveal(self): def on_reveal(self):
self.filter_hit_view.on_reveal() self.filter_hit_view.on_reveal()
class TabWidgetPlugin(PluginBase):
def __init__(self, file_view: BigText):
super(TabWidgetPlugin, self).__init__()
self._file_view = file_view
def update_hit_positions(self, hit_positions: set[float]):
self._file_view.update_hit_positions(hit_positions)

View File

@@ -119,6 +119,10 @@ class BigText(QWidget):
def get_file(self): def get_file(self):
return self.model.get_file() return self.model.get_file()
def update_hit_positions(self, hit_positions: set[float]):
if self.range_limit:
self.range_limit.update_hit_positions(hit_positions)
def add_line_click_listener(self, listener: Callable[[int], None]): def add_line_click_listener(self, listener: Callable[[int], None]):
""" """
:param listener: a callable, the parameter is the byte offset of the clicked line :param listener: a callable, the parameter is the byte offset of the clicked line

View File

@@ -4,7 +4,7 @@ from enum import Enum
import PySide6 import PySide6
from PySide6 import QtGui from PySide6 import QtGui
from PySide6.QtCore import QRect, QPoint, Signal from PySide6.QtCore import QRect, QPoint, Signal
from PySide6.QtGui import QPainter, Qt from PySide6.QtGui import QPainter, Qt, QColor, QPen
from PySide6.QtWidgets import QWidget from PySide6.QtWidgets import QWidget
from src.pluginregistry import PluginRegistry from src.pluginregistry import PluginRegistry
@@ -53,6 +53,7 @@ class RangeSlider(QWidget):
self.selected_handle = None self.selected_handle = None
self.selection_drag_range = (self.min_value, self.max_value) self.selection_drag_range = (self.min_value, self.max_value)
self.drag_y_offset_in_handle = 0 self.drag_y_offset_in_handle = 0
self._hit_positions: set[float] = set(())
def set_maximum(self, max: int): def set_maximum(self, max: int):
if self.max_value == max: if self.max_value == max:
@@ -65,12 +66,16 @@ class RangeSlider(QWidget):
self.upper_value.value = max self.upper_value.value = max
self._emit_value_changed() self._emit_value_changed()
def update_hit_positions(self, hit_positions: set[float]):
# print(f"updated hit positions in range slider:{len(hit_positions)} -> {hit_positions}")
self._hit_positions = hit_positions
def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None: def paintEvent(self, event: PySide6.QtGui.QPaintEvent) -> None:
painter = QPainter(self) painter = QPainter(self)
self._draw_background(painter) self._draw_background(painter)
self._draw_hits(painter)
self._draw_handle(painter, self.lower_value) self._draw_handle(painter, self.lower_value)
self._draw_handle(painter, self.upper_value, direction=-1) self._draw_handle(painter, self.upper_value, direction=-1)
painter.end() painter.end()
def _draw_background(self, painter: QPainter) -> None: def _draw_background(self, painter: QPainter) -> None:
@@ -111,6 +116,25 @@ class RangeSlider(QWidget):
pixel = (self.height() - 2 * self._handle_width) * value_percent + self._handle_width pixel = (self.height() - 2 * self._handle_width) * value_percent + self._handle_width
return pixel return pixel
def _draw_hits(self, painter: QPainter) -> None:
color = to_qcolor("000000")
color.setAlpha(192)
pen = QPen(color)
pen.setWidth(1)
painter.setPen(pen)
# compute where to draw a line and then deduplicate then, so that we don't draw lines multiple times
# this is for performance and because we use transparency and drawing a line multiple times would make it
# darker
paint_at_y_positions: set[int] = set(())
for hit_position in self._hit_positions:
y = (self.height() - 2 * self._handle_width) * hit_position + self._handle_width
y = round(y)
paint_at_y_positions.add(y)
for y in paint_at_y_positions:
painter.drawLine(2, y, 18, y)
def _pixel_to_value(self, pixel: int) -> int: def _pixel_to_value(self, pixel: int) -> int:
pixel_percent = (pixel - self._handle_width) / (self.height() - 2 * self._handle_width) pixel_percent = (pixel - self._handle_width) / (self.height() - 2 * self._handle_width)
return int(math.floor(self.max_value * pixel_percent)) return int(math.floor(self.max_value * pixel_percent))

View File

@@ -7,12 +7,22 @@ def is_hex_color(color: str):
return re.match("[0-9a-f]{6}", color, flags=re.IGNORECASE) return re.match("[0-9a-f]{6}", color, flags=re.IGNORECASE)
def is_hex_color_with_alpha(color: str):
return re.match("[0-9a-f]{8}", color, flags=re.IGNORECASE)
def to_qcolor(color: str): def to_qcolor(color: str):
if is_hex_color(color): if is_hex_color(color):
red = int(color[0:2], 16) red = int(color[0:2], 16)
green = int(color[2:4], 16) green = int(color[2:4], 16)
blue = int(color[4:6], 16) blue = int(color[4:6], 16)
return QColor(red, green, blue) return QColor(red, green, blue)
if is_hex_color_with_alpha(color):
red = int(color[0:2], 16)
green = int(color[2:4], 16)
blue = int(color[4:6], 16)
alpha = int(color[6:8], 16)
return QColor(red, green, blue, alpha)
elif color in QColor().colorNames(): elif color in QColor().colorNames():
return QColor(color) return QColor(color)
return QColor(255, 255, 255) return QColor(255, 255, 255)